Work on R4 for JPA server

This commit is contained in:
James 2017-08-02 09:12:38 -04:00
parent e7f8f8c30d
commit 40317a650d
96 changed files with 27458 additions and 376 deletions

View File

@ -334,8 +334,10 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
} else {
if (serverFhirVersionString.startsWith("0.4") || serverFhirVersionString.startsWith("0.5") || serverFhirVersionString.startsWith("1.0.")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2;
} else if (serverFhirVersionString.startsWith("3.")) {
} else if (serverFhirVersionString.startsWith("3.0.")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
} else if (serverFhirVersionString.startsWith("3.1.")) {
serverFhirVersionEnum = FhirVersionEnum.R4;
} else {
// we'll be lenient and accept this
ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString);

View File

@ -34,7 +34,7 @@ import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.r4.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4;
import ca.uhn.fhir.jpa.interceptor.RestHookSubscriptionR4Interceptor;
import ca.uhn.fhir.jpa.interceptor.r4.RestHookSubscriptionR4Interceptor;
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
import ca.uhn.fhir.jpa.term.*;
import ca.uhn.fhir.jpa.term.HapiTerminologySvcR4;

View File

@ -23,18 +23,15 @@ package ca.uhn.fhir.jpa.config.r4;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.config.annotation.*;
import org.springframework.web.socket.handler.PerConnectionWebSocketHandler;
import ca.uhn.fhir.jpa.interceptor.WebSocketSubscriptionDstu3Interceptor;
import ca.uhn.fhir.jpa.subscription.SubscriptionWebsocketHandlerDstu3;
import ca.uhn.fhir.jpa.interceptor.r4.WebSocketSubscriptionR4Interceptor;
import ca.uhn.fhir.jpa.subscription.r4.SubscriptionWebsocketHandlerR4;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@Configuration
@ -44,17 +41,17 @@ public class WebsocketR4Config implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry theRegistry) {
theRegistry.addHandler(subscriptionWebSocketHandler(), "/websocket/dstu3").setAllowedOrigins("*");
theRegistry.addHandler(subscriptionWebSocketHandler(), "/websocket/r4").setAllowedOrigins("*");
}
@Bean(autowire = Autowire.BY_TYPE)
public WebSocketHandler subscriptionWebSocketHandler() {
PerConnectionWebSocketHandler retVal = new PerConnectionWebSocketHandler(SubscriptionWebsocketHandlerDstu3.class);
PerConnectionWebSocketHandler retVal = new PerConnectionWebSocketHandler(SubscriptionWebsocketHandlerR4.class);
return retVal;
}
@Bean(destroyMethod="destroy")
public TaskScheduler websocketTaskSchedulerDstu3() {
public TaskScheduler websocketTaskSchedulerR4() {
final ThreadPoolTaskScheduler retVal = new ThreadPoolTaskScheduler() {
private static final long serialVersionUID = 1L;
@ -65,15 +62,15 @@ public class WebsocketR4Config implements WebSocketConfigurer {
getScheduledThreadPoolExecutor().setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
}
};
retVal.setThreadNamePrefix("ws-dstu3-");
retVal.setThreadNamePrefix("ws-r4-");
retVal.setPoolSize(5);
return retVal;
}
@Bean
public IServerInterceptor webSocketSubscriptionDstu3Interceptor(){
return new WebSocketSubscriptionDstu3Interceptor();
public IServerInterceptor webSocketSubscriptionR4Interceptor(){
return new WebSocketSubscriptionR4Interceptor();
}

View File

@ -22,14 +22,14 @@ package ca.uhn.fhir.jpa.config.r4;
import javax.annotation.PostConstruct;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.r4.model.Subscription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
import ca.uhn.fhir.jpa.subscription.SubscriptionWebsocketHandlerDstu3;
import ca.uhn.fhir.jpa.subscription.r4.SubscriptionWebsocketHandlerR4;
@Configuration
public class WebsocketR4DispatcherConfig {
@ -38,12 +38,12 @@ public class WebsocketR4DispatcherConfig {
private FhirContext myCtx;
@Autowired
private IFhirResourceDao<org.hl7.fhir.dstu3.model.Subscription> mySubscriptionDao;
private IFhirResourceDao<Subscription> mySubscriptionDao;
@PostConstruct
public void postConstruct() {
SubscriptionWebsocketHandlerDstu3.setCtx(myCtx);
SubscriptionWebsocketHandlerDstu3.setSubscriptionDao((IFhirResourceDaoSubscription<Subscription>) mySubscriptionDao);
SubscriptionWebsocketHandlerR4.setCtx(myCtx);
SubscriptionWebsocketHandlerR4.setSubscriptionDao((IFhirResourceDaoSubscription<Subscription>) mySubscriptionDao);
}
}

View File

@ -34,9 +34,9 @@ import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.*;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r4.model.BaseResource;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
@ -779,7 +779,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
List<IPrimitiveType> childElements = getContext().newTerser().getAllPopulatedChildElementsOfType(theResource, IPrimitiveType.class);
for (@SuppressWarnings("rawtypes")
IPrimitiveType nextType : childElements) {
if (nextType instanceof StringDt || nextType.getClass().equals(StringType.class)) {
if (nextType instanceof StringDt || nextType.getClass().getSimpleName().equals("StringType")) {
String nextValue = nextType.getValueAsString();
if (isNotBlank(nextValue)) {
retVal.append(nextValue.replace("\n", " ").replace("\r", " "));
@ -804,7 +804,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
private void populateResourceIdFromEntity(BaseHasResource theEntity, final IBaseResource theResource) {
IIdType id = theEntity.getIdDt();
if (getContext().getVersion().getVersion().isRi()) {
id = new IdType(id.getValue());
id = getContext().getVersion().newIdType().setValue(id.getValue());
}
theResource.setId(id);
}

View File

@ -29,7 +29,6 @@ import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
@ -990,7 +989,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
IIdType id = theEntity.getIdDt();
if (getContext().getVersion().getVersion().isRi()) {
id = new IdType(id.getValue());
id = getContext().getVersion().newIdType().setValue(id.getValue());
}
outcome.setId(id);

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
@ResourceDef(name = "Observation", profile = CustomObservationR4.PROFILE)
public class CustomObservationR4 extends Observation {
public static final String PROFILE = "http://custom_ObservationDstu3";
public static final String PROFILE = "http://custom_ObservationR4";
private static final long serialVersionUID = 1L;

View File

@ -40,7 +40,7 @@ public class FhirResourceDaoQuestionnaireResponseR4 extends FhirResourceDaoR4<Qu
// val.setValidateAgainstStandardSchema(false);
// val.setValidateAgainstStandardSchematron(false);
//
// val.registerValidatorModule(myQuestionnaireResponseValidatorDstu3);
// val.registerValidatorModule(myQuestionnaireResponseValidatorR4);
//
// ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(theResource)));
// if (!result.isSuccessful()) {

View File

@ -53,7 +53,7 @@ public class FhirResourceDaoR4<T extends IAnyResource> extends BaseHapiFhirResou
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4.class);
@Autowired()
@Qualifier("myInstanceValidatorDstu3")
@Qualifier("myInstanceValidatorR4")
private IValidatorModule myInstanceValidator;
@Override

View File

@ -49,7 +49,7 @@ import ca.uhn.fhir.util.ElementUtil;
public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Autowired
@Qualifier("myJpaValidationSupportChainDstu3")
@Qualifier("myJpaValidationSupportChainR4")
private IValidationSupport myValidationSupport;
@Autowired

View File

@ -194,7 +194,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
}
/*
* See FhirSystemDaoDstu3Test#testTransactionWithPlaceholderIdInMatchUrl
* See FhirSystemDaoR4Test#testTransactionWithPlaceholderIdInMatchUrl
* Basically if the resource has a match URL that references a placeholder,
* we try to handle the resource with the placeholder first.
*/

View File

@ -48,23 +48,23 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportR4.class);
@Autowired
@Qualifier("myStructureDefinitionDaoDstu3")
@Qualifier("myStructureDefinitionDaoR4")
private IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
@Autowired
@Qualifier("myValueSetDaoDstu3")
@Qualifier("myValueSetDaoR4")
private IFhirResourceDao<ValueSet> myValueSetDao;
@Autowired
@Qualifier("myQuestionnaireDaoDstu3")
@Qualifier("myQuestionnaireDaoR4")
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
@Autowired
@Qualifier("myCodeSystemDaoDstu3")
@Qualifier("myCodeSystemDaoR4")
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
@Autowired
private FhirContext myDstu3Ctx;
private FhirContext myR4Ctx;
public JpaValidationSupportR4() {
super();
@ -91,7 +91,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4 {
localReference = true;
}
String resourceName = myDstu3Ctx.getResourceDefinition(theClass).getName();
String resourceName = myR4Ctx.getResourceDefinition(theClass).getName();
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
if (localReference) {

View File

@ -1,5 +1,5 @@
package ca.uhn.fhir.jpa.interceptor;
package ca.uhn.fhir.jpa.interceptor.r4;
/*-
* #%L
@ -43,6 +43,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.interceptor.BaseRestHookSubscriptionInterceptor;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.jpa.thread.HttpRequestR4Job;
import ca.uhn.fhir.rest.api.*;
@ -63,7 +64,7 @@ public class RestHookSubscriptionR4Interceptor extends BaseRestHookSubscriptionI
private final List<Subscription> myRestHookSubscriptions = new ArrayList<Subscription>();
@Autowired
@Qualifier("mySubscriptionDaoDstu3")
@Qualifier("mySubscriptionDaoR4")
private IFhirResourceDao<Subscription> mySubscriptionDao;
private boolean notifyOnDelete = false;

View File

@ -0,0 +1,107 @@
package ca.uhn.fhir.jpa.interceptor.r4;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
public class WebSocketSubscriptionR4Interceptor extends ServerOperationInterceptorAdapter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebSocketSubscriptionR4Interceptor.class);
private IFhirResourceDaoSubscription<Subscription> mySubscriptionDaoCasted;
@Autowired
@Qualifier("mySubscriptionDaoR4")
private IFhirResourceDao<Subscription> mySubscriptionDao;
@Override
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
if (theRequestDetails.getRestOperationType().equals(RestOperationTypeEnum.DELETE)) {
mySubscriptionDaoCasted.pollForNewUndeliveredResources(theRequestDetails.getResourceName());
}
return super.incomingRequestPostProcessed(theRequestDetails, theRequest, theResponse);
}
/**
* Checks for websocket subscriptions
*
* @param theRequestDetails
* A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the {@link HttpServletRequest servlet request}.
* @param theResponseObject
* The actual object which is being streamed to the client as a response
* @return
*/
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
if (theRequestDetails.getResourceName() == null ||
theRequestDetails.getResourceName().isEmpty() ||
theRequestDetails.getResourceName().equals("Subscription")) {
return super.outgoingResponse(theRequestDetails, theResponseObject);
}
if (theRequestDetails.getRequestType().equals(RequestTypeEnum.POST) || theRequestDetails.getRequestType().equals(RequestTypeEnum.PUT)) {
ourLog.info("Found POST or PUT for a non-subscription resource");
mySubscriptionDaoCasted.pollForNewUndeliveredResources(theRequestDetails.getResourceName());
}
return super.outgoingResponse(theRequestDetails, theResponseObject);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@PostConstruct
public void postConstruct() {
mySubscriptionDaoCasted = (IFhirResourceDaoSubscription) mySubscriptionDao;
}
@Override
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
// nothing
}
@Override
public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) {
// nothing
}
@Override
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
// nothing
}
}

View File

@ -42,7 +42,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
public class JpaSystemProviderR4 extends BaseJpaSystemProviderDstu2Plus<Bundle, Meta> {
@Autowired()
@Qualifier("mySystemDaoDstu3")
@Qualifier("mySystemDaoR4")
private IFhirSystemDao<Bundle, Meta> mySystemDao;
@Autowired

View File

@ -0,0 +1,348 @@
package ca.uhn.fhir.jpa.subscription.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
import ca.uhn.fhir.jpa.subscription.ISubscriptionWebsocketHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class SubscriptionWebsocketHandlerR4 extends TextWebSocketHandler implements ISubscriptionWebsocketHandler, Runnable {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketHandlerR4.class);
private static IFhirResourceDaoSubscription<Subscription> ourSubscriptionDao;
private ScheduledFuture<?> myScheduleFuture;
private IState myState = new InitialState();
private IIdType mySubscriptionId;
private Long mySubscriptionPid;
@Autowired
@Qualifier("websocketTaskSchedulerR4")
private TaskScheduler myTaskScheduler;
@Override
public void afterConnectionClosed(WebSocketSession theSession, CloseStatus theStatus) throws Exception {
super.afterConnectionClosed(theSession, theStatus);
ourLog.info("Closing WebSocket connection from {}", theSession.getRemoteAddress());
}
@Override
public void afterConnectionEstablished(WebSocketSession theSession) throws Exception {
super.afterConnectionEstablished(theSession);
ourLog.info("Incoming WebSocket connection from {}", theSession.getRemoteAddress());
}
protected void handleFailure(Exception theE) {
ourLog.error("Failure during communication", theE);
}
@Override
protected void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) throws Exception {
ourLog.info("Textmessage: " + theMessage.getPayload());
myState.handleTextMessage(theSession, theMessage);
}
@Override
public void handleTransportError(WebSocketSession theSession, Throwable theException) throws Exception {
super.handleTransportError(theSession, theException);
ourLog.error("Transport error", theException);
}
@PostConstruct
public void postConstruct() {
ourLog.info("Creating scheduled task for subscription websocket connection");
myScheduleFuture = myTaskScheduler.scheduleWithFixedDelay(this, 1000);
}
@PreDestroy
public void preDescroy() {
ourLog.info("Cancelling scheduled task for subscription websocket connection");
myScheduleFuture.cancel(true);
IState state = myState;
if (state != null) {
state.closing();
}
}
@Override
public void run() {
Long subscriptionPid = mySubscriptionPid;
if (subscriptionPid == null) {
return;
}
ourLog.debug("Subscription {} websocket handler polling", subscriptionPid);
List<IBaseResource> results = ourSubscriptionDao.getUndeliveredResourcesAndPurge(subscriptionPid);
if (results.isEmpty() == false) {
myState.deliver(results);
}
}
public static void setCtx(FhirContext theCtx) {
ourCtx = theCtx;
}
public static void setSubscriptionDao(IFhirResourceDaoSubscription<Subscription> theSubscriptionDao) {
ourSubscriptionDao = theSubscriptionDao;
}
private class BoundDynamicSubscriptionState implements IState {
private EncodingEnum myEncoding;
private WebSocketSession mySession;
public BoundDynamicSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) {
mySession = theSession;
myEncoding = theEncoding;
}
@Override
public void closing() {
ourLog.info("Deleting subscription {}", mySubscriptionId);
try {
ourSubscriptionDao.delete(mySubscriptionId, null);
} catch (Exception e) {
handleFailure(e);
}
}
@Override
public void deliver(List<IBaseResource> theResults) {
try {
for (IBaseResource nextResource : theResults) {
ourLog.info("Sending WebSocket message for resource: {}", nextResource.getIdElement());
String encoded = myEncoding.newParser(ourCtx).encodeResourceToString(nextResource);
String payload = "add " + mySubscriptionId.getIdPart() + '\n' + encoded;
mySession.sendMessage(new TextMessage(payload));
}
} catch (IOException e) {
handleFailure(e);
}
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
try {
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
} catch (IOException e) {
handleFailure(e);
}
}
}
private class BoundStaticSubscipriptionState implements IState {
private WebSocketSession mySession;
public BoundStaticSubscipriptionState(WebSocketSession theSession) {
mySession = theSession;
}
@Override
public void closing() {
// nothing
}
@Override
public void deliver(List<IBaseResource> theResults) {
try {
String payload = "ping " + mySubscriptionId.getIdPart();
ourLog.info("Sending WebSocket message: {}", payload);
mySession.sendMessage(new TextMessage(payload));
} catch (IOException e) {
handleFailure(e);
}
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
try {
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
} catch (IOException e) {
handleFailure(e);
}
}
}
private class InitialState implements IState {
private IIdType bindSimple(WebSocketSession theSession, String theBindString) {
IdType id = new IdType(theBindString);
if (!id.hasIdPart() || !id.isIdPartValid()) {
try {
String message = "Invalid bind request - No ID included";
ourLog.warn(message);
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), message));
} catch (IOException e) {
handleFailure(e);
}
return null;
}
if (id.hasResourceType() == false) {
id = id.withResourceType("Subscription");
}
try {
Subscription subscription = ourSubscriptionDao.read(id, null);
mySubscriptionPid = ourSubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
mySubscriptionId = subscription.getIdElement();
myState = new BoundStaticSubscipriptionState(theSession);
} catch (ResourceNotFoundException e) {
try {
String message = "Invalid bind request - Unknown subscription: " + id.getValue();
ourLog.warn(message);
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), message));
} catch (IOException e1) {
handleFailure(e);
}
return null;
}
return id;
}
private IIdType bingSearch(WebSocketSession theSession, String theRemaining) {
Subscription subscription = new Subscription();
subscription.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subscription.setStatus(SubscriptionStatus.ACTIVE);
subscription.setCriteria(theRemaining);
try {
String params = theRemaining.substring(theRemaining.indexOf('?')+1);
List<NameValuePair> paramValues = URLEncodedUtils.parse(params, Constants.CHARSET_UTF8, '&');
EncodingEnum encoding = EncodingEnum.JSON;
for (NameValuePair nameValuePair : paramValues) {
if (Constants.PARAM_FORMAT.equals(nameValuePair.getName())) {
EncodingEnum nextEncoding = EncodingEnum.forContentType(nameValuePair.getValue());
if (nextEncoding != null) {
encoding = nextEncoding;
}
}
}
IIdType id = ourSubscriptionDao.create(subscription).getId();
mySubscriptionPid = ourSubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
mySubscriptionId = subscription.getIdElement();
myState = new BoundDynamicSubscriptionState(theSession, encoding);
return id;
} catch (UnprocessableEntityException e) {
ourLog.warn("Failed to bind subscription: " + e.getMessage());
try {
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - " + e.getMessage()));
} catch (IOException e2) {
handleFailure(e2);
}
} catch (Exception e) {
handleFailure(e);
try {
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
} catch (IOException e2) {
handleFailure(e2);
}
}
return null;
}
@Override
public void closing() {
// nothing
}
@Override
public void deliver(List<IBaseResource> theResults) {
throw new IllegalStateException();
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
String message = theMessage.getPayload();
if (message.startsWith("bind ")) {
String remaining = message.substring("bind ".length());
IIdType subscriptionId;
if (remaining.contains("?")) {
subscriptionId = bingSearch(theSession, remaining);
} else {
subscriptionId = bindSimple(theSession, remaining);
if (subscriptionId == null) {
return;
}
}
try {
theSession.sendMessage(new TextMessage("bound " + subscriptionId.getIdPart()));
} catch (IOException e) {
handleFailure(e);
}
}
}
}
private interface IState {
void closing();
void deliver(List<IBaseResource> theResults);
void handleTextMessage(WebSocketSession theSession, TextMessage theMessage);
}
}

View File

@ -0,0 +1,361 @@
package ca.uhn.fhir.jpa.subscription.r4;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Subscription;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
import ca.uhn.fhir.jpa.subscription.ISubscriptionWebsocketHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class SubscriptionWebsocketReturnResourceHandlerR4 extends TextWebSocketHandler implements ISubscriptionWebsocketHandler, Runnable {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionWebsocketReturnResourceHandlerR4.class);
@Autowired
private FhirContext myCtx;
private ScheduledFuture<?> myScheduleFuture;
private IState myState = new InitialState();
@Autowired
private IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
private IIdType mySubscriptionId;
private Long mySubscriptionPid;
@Autowired
@Qualifier("websocketTaskScheduler")
private TaskScheduler myTaskScheduler;
@Override
public void afterConnectionClosed(WebSocketSession theSession, CloseStatus theStatus) throws Exception {
super.afterConnectionClosed(theSession, theStatus);
ourLog.info("Closing WebSocket connection from {}", theSession.getRemoteAddress());
}
@Override
public void afterConnectionEstablished(WebSocketSession theSession) throws Exception {
super.afterConnectionEstablished(theSession);
ourLog.info("Incoming WebSocket connection from {}", theSession.getRemoteAddress());
}
protected void handleFailure(Exception theE) {
ourLog.error("Failure during communication", theE);
}
@Override
protected void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) throws Exception {
ourLog.info("Textmessage: " + theMessage.getPayload());
myState.handleTextMessage(theSession, theMessage);
}
@Override
public void handleTransportError(WebSocketSession theSession, Throwable theException) throws Exception {
super.handleTransportError(theSession, theException);
ourLog.error("Transport error", theException);
}
@PostConstruct
public void postConstruct() {
ourLog.info("Creating scheduled task for subscription websocket connection");
myScheduleFuture = myTaskScheduler.scheduleWithFixedDelay(this, 1000);
}
@PreDestroy
public void preDescroy() {
ourLog.info("Cancelling scheduled task for subscription websocket connection");
myScheduleFuture.cancel(true);
IState state = myState;
if (state != null) {
state.closing();
}
}
@Override
public void run() {
Long subscriptionPid = mySubscriptionPid;
if (subscriptionPid == null) {
return;
}
ourLog.debug("Subscription {} websocket handler polling", subscriptionPid);
List<IBaseResource> results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subscriptionPid);
if (results.isEmpty() == false) {
myState.deliver(results);
}
}
private class BoundDynamicSubscriptionState implements IState {
private EncodingEnum myEncoding;
private WebSocketSession mySession;
public BoundDynamicSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) {
mySession = theSession;
myEncoding = theEncoding;
}
@Override
public void closing() {
ourLog.info("Deleting subscription {}", mySubscriptionId);
try {
mySubscriptionDao.delete(mySubscriptionId, null);
} catch (Exception e) {
handleFailure(e);
}
}
@Override
public void deliver(List<IBaseResource> theResults) {
try {
for (IBaseResource nextResource : theResults) {
ourLog.info("Sending WebSocket message for resource: {}", nextResource.getIdElement());
String encoded = myEncoding.newParser(myCtx).encodeResourceToString(nextResource);
String payload = "add " + mySubscriptionId.getIdPart() + '\n' + encoded;
mySession.sendMessage(new TextMessage(payload));
}
} catch (IOException e) {
handleFailure(e);
}
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
try {
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
} catch (IOException e) {
handleFailure(e);
}
}
}
private class BoundStaticSubscriptionState implements IState {
private EncodingEnum myEncoding;
private WebSocketSession mySession;
public BoundStaticSubscriptionState(WebSocketSession theSession, EncodingEnum theEncoding) {
mySession = theSession;
myEncoding = theEncoding;
}
@Override
public void closing() {
// nothing
}
@Override
public void deliver(List<IBaseResource> theResults) {
try {
for (IBaseResource nextResource : theResults) {
ourLog.info("Sending WebSocket message for resource: {}", nextResource.getIdElement());
String encoded = myEncoding.newParser(myCtx).encodeResourceToString(nextResource);
String payload = "add " + mySubscriptionId.getIdPart() + '\n' + encoded;
mySession.sendMessage(new TextMessage(payload));
}
} catch (IOException e) {
handleFailure(e);
}
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
try {
theSession.sendMessage(new TextMessage("Unexpected client message: " + theMessage.getPayload()));
} catch (IOException e) {
handleFailure(e);
}
}
}
private class InitialState implements IState {
private IIdType bindSimple(WebSocketSession theSession, String theBindString) {
IdType id = new IdType(theBindString);
if (!id.hasIdPart() || !id.isIdPartValid()) {
try {
String message = "Invalid bind request - No ID included";
ourLog.warn(message);
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), message));
} catch (IOException e) {
handleFailure(e);
}
return null;
}
if (id.hasResourceType() == false) {
id = id.withResourceType("Subscription");
}
try {
Subscription subscription = mySubscriptionDao.read(id, null);
EncodingEnum encoding = EncodingEnum.JSON;
String criteria = subscription.getCriteria();
String params = criteria.substring(criteria.indexOf('?') + 1);
List<NameValuePair> paramValues = URLEncodedUtils.parse(params, Constants.CHARSET_UTF8, '&');
for (NameValuePair nameValuePair : paramValues) {
if (Constants.PARAM_FORMAT.equals(nameValuePair.getName())) {
EncodingEnum nextEncoding = EncodingEnum.forContentType(nameValuePair.getValue());
if (nextEncoding != null) {
encoding = nextEncoding;
}
}
}
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
mySubscriptionId = subscription.getIdElement();
myState = new BoundStaticSubscriptionState(theSession, encoding);
} catch (ResourceNotFoundException e) {
try {
String message = "Invalid bind request - Unknown subscription: " + id.getValue();
ourLog.warn(message);
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), message));
} catch (IOException e1) {
handleFailure(e);
}
return null;
}
return id;
}
private IIdType bindSearch(WebSocketSession theSession, String theRemaining) {
Subscription subscription = new Subscription();
subscription.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subscription.setStatus(SubscriptionStatus.ACTIVE);
subscription.setCriteria(theRemaining);
try {
String params = theRemaining.substring(theRemaining.indexOf('?')+1);
List<NameValuePair> paramValues = URLEncodedUtils.parse(params, Constants.CHARSET_UTF8, '&');
EncodingEnum encoding = EncodingEnum.JSON;
for (NameValuePair nameValuePair : paramValues) {
if (Constants.PARAM_FORMAT.equals(nameValuePair.getName())) {
EncodingEnum nextEncoding = EncodingEnum.forContentType(nameValuePair.getValue());
if (nextEncoding != null) {
encoding = nextEncoding;
}
}
}
IIdType id = mySubscriptionDao.create(subscription).getId();
mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id);
mySubscriptionId = subscription.getIdElement();
myState = new BoundDynamicSubscriptionState(theSession, encoding);
return id;
} catch (UnprocessableEntityException e) {
ourLog.warn("Failed to bind subscription: " + e.getMessage());
try {
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - " + e.getMessage()));
} catch (IOException e2) {
handleFailure(e2);
}
} catch (Exception e) {
handleFailure(e);
try {
theSession.close(new CloseStatus(CloseStatus.PROTOCOL_ERROR.getCode(), "Invalid bind request - No ID included"));
} catch (IOException e2) {
handleFailure(e2);
}
}
return null;
}
@Override
public void closing() {
// nothing
}
@Override
public void deliver(List<IBaseResource> theResults) {
throw new IllegalStateException();
}
@Override
public void handleTextMessage(WebSocketSession theSession, TextMessage theMessage) {
String message = theMessage.getPayload();
if (message.startsWith("bind ")) {
String remaining = message.substring("bind ".length());
IIdType subscriptionId;
if (remaining.contains("?")) {
subscriptionId = bindSearch(theSession, remaining);
} else {
subscriptionId = bindSimple(theSession, remaining);
if (subscriptionId == null) {
return;
}
}
try {
theSession.sendMessage(new TextMessage("bound " + subscriptionId.getIdPart()));
} catch (IOException e) {
handleFailure(e);
}
}
}
}
private interface IState {
void closing();
void deliver(List<IBaseResource> theResults);
void handleTextMessage(WebSocketSession theSession, TextMessage theMessage);
}
}

View File

@ -40,12 +40,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -57,7 +57,8 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HapiTerminologySvcR4.class);
@Autowired
private IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemResourceDao;
@Qualifier("myCodeSystemDaoR4")
private IFhirResourceDao<CodeSystem> myCodeSystemResourceDao;
@Autowired
private IValidationSupport myValidationSupport;

View File

@ -19,6 +19,7 @@ public class TestDstu3WithoutLuceneConfig extends TestDstu3Config {
/**
* Disable fulltext searching
*/
@Override
public IFulltextSearchSvc searchDaoDstu3() {
return null;
}

View File

@ -0,0 +1,150 @@
package ca.uhn.fhir.jpa.config;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.*;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
@Configuration
@EnableTransactionManagement()
public class TestR4Config extends BaseJavaConfigR4 {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class);
@Bean()
public DaoConfig daoConfig() {
return new DaoConfig();
}
private Exception myLastStackTrace;
@Bean()
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource() {
@Override
public Connection getConnection() throws SQLException {
ConnectionWrapper retVal;
try {
retVal = new ConnectionWrapper(super.getConnection());
} catch (Exception e) {
ourLog.error("Exceeded maximum wait for connection", e);
logGetConnectionStackTrace();
System.exit(1);
retVal = null;
}
try {
throw new Exception();
} catch (Exception e) {
myLastStackTrace = e;
}
return retVal;
}
private void logGetConnectionStackTrace() {
StringBuilder b = new StringBuilder();
b.append("Last connection request stack trace:");
for (StackTraceElement next : myLastStackTrace.getStackTrace()) {
b.append("\n ");
b.append(next.getClassName());
b.append(".");
b.append(next.getMethodName());
b.append("(");
b.append(next.getFileName());
b.append(":");
b.append(next.getLineNumber());
b.append(")");
}
ourLog.info(b.toString());
}
};
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");
/*
* We use a randomized number of maximum threads in order to try
* and catch any potential deadlocks caused by database connection
* starvation
*/
int maxThreads = (int) (Math.random() * 6) + 1;
retVal.setMaxTotal(maxThreads);
DataSource dataSource = ProxyDataSourceBuilder
.create(retVal)
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
.countQuery()
.build();
return dataSource;
}
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
retVal.setPersistenceUnitName("PU_HapiFhirJpaR4");
retVal.setDataSource(dataSource());
retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
retVal.setPersistenceProvider(new HibernatePersistenceProvider());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.jdbc.batch_size", "50");
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
extraProperties.put("hibernate.search.default.directory_provider", "ram");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
return extraProperties;
}
/**
* Bean which validates incoming requests
*/
@Bean
@Lazy
public RequestValidatingInterceptor requestValidatingInterceptor() {
RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor();
requestValidator.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestValidator.setAddResponseHeaderOnSeverity(null);
requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestValidator.addValidatorModule(instanceValidatorR4());
return requestValidator;
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
}

View File

@ -0,0 +1,49 @@
package ca.uhn.fhir.jpa.config;
import java.util.Properties;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
@Configuration
@EnableTransactionManagement()
public class TestR4WithoutLuceneConfig extends TestR4Config {
/**
* Disable fulltext searching
*/
@Override
public IFulltextSearchSvc searchDaoR4() {
return null;
}
@Override
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
retVal.setPersistenceUnitName("PU_HapiFhirJpaR4");
retVal.setDataSource(dataSource());
retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
retVal.setPersistenceProvider(new HibernatePersistenceProvider());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
extraProperties.put("hibernate.search.autoregister_listeners", "false");
return extraProperties;
}
}

View File

@ -67,6 +67,14 @@ public abstract class BaseJpaTest {
return bundle;
}
protected org.hl7.fhir.r4.model.Bundle toBundleR4(IBundleProvider theSearch) {
org.hl7.fhir.r4.model.Bundle bundle = new org.hl7.fhir.r4.model.Bundle();
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
bundle.addEntry().setResource((org.hl7.fhir.r4.model.Resource) next);
}
return bundle;
}
protected abstract FhirContext getContext();
protected List<String> toUnqualifiedVersionlessIdValues(IBaseBundle theFound) {
@ -132,6 +140,16 @@ public abstract class BaseJpaTest {
return retVal;
}
protected List<IIdType> toUnqualifiedVersionlessIds(org.hl7.fhir.r4.model.Bundle theFound) {
List<IIdType> retVal = new ArrayList<IIdType>();
for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent next : theFound.getEntry()) {
// if (next.getResource()!= null) {
retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless());
// }
}
return retVal;
}
protected List<String> toUnqualifiedVersionlessIdValues(IBundleProvider theFound) {
List<String> retVal = new ArrayList<String>();
int size = theFound.size();

View File

@ -0,0 +1,54 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.junit.AfterClass;
import org.junit.Before;
import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider;
import ca.uhn.fhir.rest.api.server.IRequestOperationCallback;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public abstract class BaseJpaR4SystemTest extends BaseJpaR4Test {
protected ServletRequestDetails mySrd;
private RestfulServer myServer;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@SuppressWarnings("unchecked")
@Before
public void before() throws ServletException {
mySrd = mock(ServletRequestDetails.class);
when(mySrd.getRequestOperationCallback()).thenReturn(mock(IRequestOperationCallback.class));
if (myServer == null) {
myServer = new RestfulServer(myFhirCtx);
PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(myPatientDao);
myServer.setResourceProviders(patientRp);
myServer.init(mock(ServletConfig.class));
}
when(mySrd.getServer()).thenReturn(myServer);
HttpServletRequest servletRequest = mock(HttpServletRequest.class);
when(mySrd.getServletRequest()).thenReturn(servletRequest);
when(mySrd.getFhirServerBase()).thenReturn("http://example.com/base");
when(servletRequest.getHeaderNames()).thenReturn(mock(Enumeration.class));
when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("/Patient"));
}
}

View File

@ -0,0 +1,301 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import javax.persistence.EntityManager;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.TestR4Config;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
import ca.uhn.fhir.jpa.search.*;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
//@formatter:off
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= {TestR4Config.class})
//@formatter:on
public abstract class BaseJpaR4Test extends BaseJpaTest {
private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4;
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
// @Autowired
// protected HapiWorkerContext myHapiWorkerContext;
@Autowired
@Qualifier("myAllergyIntoleranceDaoR4")
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
@Autowired
protected ApplicationContext myAppCtx;
@Autowired
@Qualifier("myAppointmentDaoR4")
protected IFhirResourceDao<Appointment> myAppointmentDao;
@Autowired
@Qualifier("myAuditEventDaoR4")
protected IFhirResourceDao<AuditEvent> myAuditEventDao;
@Autowired
@Qualifier("myBundleDaoR4")
protected IFhirResourceDao<Bundle> myBundleDao;
@Autowired
@Qualifier("myCarePlanDaoR4")
protected IFhirResourceDao<CarePlan> myCarePlanDao;
@Autowired
@Qualifier("myCodeSystemDaoR4")
protected IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemDao;
@Autowired
@Qualifier("myCompartmentDefinitionDaoR4")
protected IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@Qualifier("myConceptMapDaoR4")
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
@Autowired
@Qualifier("myConditionDaoR4")
protected IFhirResourceDao<Condition> myConditionDao;
@Autowired
protected DaoConfig myDaoConfig;
@Autowired
@Qualifier("myDeviceDaoR4")
protected IFhirResourceDao<Device> myDeviceDao;
@Autowired
@Qualifier("myDiagnosticReportDaoR4")
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
@Autowired
@Qualifier("myEncounterDaoR4")
protected IFhirResourceDao<Encounter> myEncounterDao;
// @PersistenceContext()
@Autowired
protected EntityManager myEntityManager;
@Autowired
protected FhirContext myFhirCtx;
@Autowired
@Qualifier("myGroupDaoR4")
protected IFhirResourceDao<Group> myGroupDao;
@Autowired
@Qualifier("myImmunizationDaoR4")
protected IFhirResourceDao<Immunization> myImmunizationDao;
@Autowired
@Qualifier("myImmunizationRecommendationDaoR4")
protected IFhirResourceDao<ImmunizationRecommendation> myImmunizationRecommendationDao;
protected IServerInterceptor myInterceptor;
@Autowired
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
@Autowired
@Qualifier("myLocationDaoR4")
protected IFhirResourceDao<Location> myLocationDao;
@Autowired
@Qualifier("myMediaDaoR4")
protected IFhirResourceDao<Media> myMediaDao;
@Autowired
@Qualifier("myMedicationAdministrationDaoR4")
protected IFhirResourceDao<MedicationAdministration> myMedicationAdministrationDao;
@Autowired
@Qualifier("myMedicationDaoR4")
protected IFhirResourceDao<Medication> myMedicationDao;
@Autowired
@Qualifier("myMedicationRequestDaoR4")
protected IFhirResourceDao<MedicationRequest> myMedicationRequestDao;
@Autowired
@Qualifier("myNamingSystemDaoR4")
protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
@Autowired
@Qualifier("myObservationDaoR4")
protected IFhirResourceDao<Observation> myObservationDao;
@Autowired
@Qualifier("myOperationDefinitionDaoR4")
protected IFhirResourceDao<OperationDefinition> myOperationDefinitionDao;
@Autowired
@Qualifier("myOrganizationDaoR4")
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
protected DatabaseBackedPagingProvider myPagingProvider;
@Autowired
@Qualifier("myPatientDaoR4")
protected IFhirResourceDaoPatient<Patient> myPatientDao;
@Autowired
@Qualifier("myPractitionerDaoR4")
protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired
@Qualifier("myProcedureRequestDaoR4")
protected IFhirResourceDao<ProcedureRequest> myProcedureRequestDao;
@Autowired
@Qualifier("myQuestionnaireDaoR4")
protected IFhirResourceDao<Questionnaire> myQuestionnaireDao;
@Autowired
@Qualifier("myQuestionnaireResponseDaoR4")
protected IFhirResourceDao<QuestionnaireResponse> myQuestionnaireResponseDao;
@Autowired
@Qualifier("myResourceProvidersR4")
protected Object myResourceProviders;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected IResourceTagDao myResourceTagDao;
@Autowired
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
@Autowired
protected IFulltextSearchSvc mySearchDao;
@Autowired
protected ISearchDao mySearchEntityDao;
@Autowired
@Qualifier("mySearchParameterDaoR4")
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
@Autowired
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Autowired
protected ISearchParamRegistry mySearchParamRegsitry;
@Autowired
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
@Autowired
@Qualifier("myStructureDefinitionDaoR4")
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
@Autowired
@Qualifier("mySubscriptionDaoR4")
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
@Autowired
@Qualifier("mySubstanceDaoR4")
protected IFhirResourceDao<Substance> mySubstanceDao;
@Autowired
@Qualifier("mySystemDaoR4")
protected IFhirSystemDao<Bundle, Meta> mySystemDao;
@Autowired
@Qualifier("mySystemProviderR4")
protected JpaSystemProviderR4 mySystemProvider;
@Autowired
protected ITagDefinitionDao myTagDefinitionDao;
@Autowired
@Qualifier("myTaskDaoR4")
protected IFhirResourceDao<Task> myTaskDao;
@Autowired
protected IHapiTerminologySvc myTermSvc;
@Autowired
protected PlatformTransactionManager myTransactionMgr;
@Autowired
protected PlatformTransactionManager myTxManager;
@Autowired
@Qualifier("myJpaValidationSupportChainR4")
protected IValidationSupport myValidationSupport;
@Autowired
@Qualifier("myValueSetDaoR4")
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
@After()
public void afterCleanupDao() {
myDaoConfig.setExpireSearchResults(new DaoConfig().isExpireSearchResults());
myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis());
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange());
}
@After()
public void afterGrabCaches() {
ourValueSetDao = myValueSetDao;
ourJpaValidationSupportChainR4 = myJpaValidationSupportChainR4;
}
@Before
public void beforeCreateInterceptor() {
myInterceptor = mock(IServerInterceptor.class);
myDaoConfig.setInterceptors(myInterceptor);
}
@Before
@Transactional
public void beforeFlushFT() {
FullTextEntityManager ftem = Search.getFullTextEntityManager(myEntityManager);
ftem.purgeAll(ResourceTable.class);
ftem.purgeAll(ResourceIndexedSearchParamString.class);
ftem.flushToIndexes();
myDaoConfig.setSchedulingDisabled(true);
}
@Before
@Transactional()
public void beforePurgeDatabase() {
final EntityManager entityManager = this.myEntityManager;
purgeDatabase(entityManager, myTxManager, mySearchParamPresenceSvc, mySearchCoordinatorSvc);
}
@Before
public void beforeResetConfig() {
myDaoConfig.setHardSearchLimit(1000);
myDaoConfig.setHardTagListLimit(1000);
myDaoConfig.setIncludeLimit(2000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
}
@Override
protected FhirContext getContext() {
return myFhirCtx;
}
protected <T extends IBaseResource> T loadResourceFromClasspath(Class<T> type, String resourceName) throws IOException {
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
if (stream == null) {
fail("Unable to load resource: " + resourceName);
}
String string = IOUtils.toString(stream, "UTF-8");
IParser newJsonParser = EncodingEnum.detectEncodingNoDefault(string).newParser(myFhirCtx);
return newJsonParser.parseResource(type, string);
}
public TransactionTemplate newTxTemplate() {
TransactionTemplate retVal = new TransactionTemplate(myTxManager);
retVal.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
retVal.afterPropertiesSet();
return retVal;
}
@AfterClass
public static void afterClassClearContextBaseJpaR4Test() throws Exception {
ourValueSetDao.purgeCaches();
ourJpaValidationSupportChainR4.flush();
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static String toSearchUuidFromLinkNext(Bundle theBundle) {
String linkNext = theBundle.getLink("next").getUrl();
linkNext = linkNext.substring(linkNext.indexOf('?'));
Map<String, String[]> params = UrlUtil.parseQueryString(linkNext);
String[] uuidParams = params.get(Constants.PARAM_PAGINGACTION);
String uuid = uuidParams[0];
return uuid;
}
}

View File

@ -0,0 +1,43 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.assertEquals;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@SuppressWarnings({ })
public class FhirResourceDaoCustomTypeR4Test extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCustomTypeR4Test.class);
@Before
public void before() {
myFhirCtx.setDefaultTypeForProfile(CustomObservationR4.PROFILE, CustomObservationR4.class);
}
@Test
public void testSaveAndRestore() {
CustomObservationR4 obs = new CustomObservationR4();
obs.setEyeColour(new StringType("blue"));
IIdType id = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
CustomObservationR4 read = (CustomObservationR4) myObservationDao.read(id);
assertEquals("blue", read.getEyeColour().getValue());
IBundleProvider found = myObservationDao.search(new SearchParameterMap());
assertEquals(1, found.size().intValue());
CustomObservationR4 search = (CustomObservationR4) found.getResources(0, 1).get(0);
assertEquals("blue", search.getEyeColour().getValue());
}
@After
public void after() {
myFhirCtx.setDefaultTypeForProfile(CustomObservationR4.PROFILE, null);
}
}

View File

@ -0,0 +1,31 @@
package ca.uhn.fhir.jpa.dao.r4;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r4.model.Bundle;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDocumentR4Test extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDocumentR4Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testPostDocument() throws Exception {
String input = IOUtils.toString(getClass().getResourceAsStream("/sample-document.xml"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
DaoMethodOutcome responseBundle = myBundleDao.create(inputBundle, mySrd);
}
}

View File

@ -0,0 +1,44 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.assertNotEquals;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r4.model.CodeSystem;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4CodeSystemTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Test
public void testIndexContained() throws Exception {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
String input = IOUtils.toString(getClass().getResource("/r4_codesystem_complete.json"), StandardCharsets.UTF_8);
CodeSystem cs = myFhirCtx.newJsonParser().parseResource(CodeSystem.class, input);
myCodeSystemDao.create(cs, mySrd);
mySystemDao.markAllResourcesForReindexing();
int outcome = mySystemDao.performReindexingPass(100);
assertNotEquals(-1, outcome); // -1 means there was a failure
myTermSvc.saveDeferred();
}
}

View File

@ -0,0 +1,61 @@
package ca.uhn.fhir.jpa.dao.r4;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ContainedTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void before() {
myDaoConfig.setIndexContainedResources(true);
}
@Test
public void testIndexContained() {
Patient p = new Patient();
p.setId("#some_patient");
p.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
Observation o1 = new Observation();
o1.getCode().setText("Some Observation");
o1.setSubject(new Reference(p));
IIdType oid1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
Observation o2 = new Observation();
o2.getCode().setText("Some Observation");
o2.setSubject(new Reference(p));
IIdType oid2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
IIdType pid2 = myPatientDao.create(p2, mySrd).getId().toUnqualifiedVersionless();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2));
SearchParameterMap map;
// map = new SearchParameterMap();
// map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
}
// TODO: make sure match URLs don't delete
}

View File

@ -0,0 +1,163 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.util.HashSet;
import java.util.Set;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4ExternalReferenceTest extends BaseJpaR4Test {
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@After
public void resetDefaultBehaviour() {
// Reset to default
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setTreatBaseUrlsAsLocal(null);
}
@Test
public void testInternalReferenceBlockedByDefault() {
Patient p = new Patient();
p.getManagingOrganization().setReference("Organization/FOO");
try {
myPatientDao.create(p, mySrd);
fail();
} catch (InvalidRequestException e) {
assertEquals("Resource Organization/FOO not found, specified in path: Patient.managingOrganization", e.getMessage());
}
}
@Test
public void testExternalReferenceBlockedByDefault() {
Organization org = new Organization();
org.setId("FOO");
org.setName("Org Name");
myOrganizationDao.update(org, mySrd);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
try {
myPatientDao.create(p, mySrd);
fail();
} catch (InvalidRequestException e) {
assertEquals("Resource contains external reference to URL \"http://example.com/base/Organization/FOO\" but this server is not configured to allow external references", e.getMessage());
}
}
@Test
public void testExternalReferenceAllowed() {
Organization org = new Organization();
org.setId("FOO");
org.setName("Org Name");
myOrganizationDao.update(org, mySrd);
myDaoConfig.setAllowExternalReferences(true);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example.com/base/Organization/FOO"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(pid.getValue()));
map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example2.com/base/Organization/FOO"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
}
@Test
public void testExternalReferenceReplaced() {
Organization org = new Organization();
org.setId("FOO");
org.setName("Org Name");
org.getPartOf().setDisplay("Parent"); // <-- no reference, make sure this works
myOrganizationDao.update(org, mySrd);
Set<String> urls = new HashSet<String>();
urls.add("http://example.com/base/");
myDaoConfig.setTreatBaseUrlsAsLocal(urls);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
p = myPatientDao.read(pid, mySrd);
assertEquals("Organization/FOO", p.getManagingOrganization().getReference());
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example.com/base/Organization/FOO"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(pid.getValue()));
}
@Test
public void testSearchForInvalidLocalReference() {
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization/FOO"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization/9999999999"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
}
@Test
public void testExternalReferenceReplacedWrongDoesntMatch() {
Organization org = new Organization();
org.setId("FOO");
org.setName("Org Name");
org.getPartOf().setDisplay("Parent"); // <-- no reference, make sure this works
myOrganizationDao.update(org, mySrd);
Set<String> urls = new HashSet<String>();
urls.add("http://example.com/base/");
myDaoConfig.setTreatBaseUrlsAsLocal(urls);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
p = myPatientDao.read(pid, mySrd);
assertEquals("Organization/FOO", p.getManagingOrganization().getReference());
SearchParameterMap map;
// Different base
map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://foo.com/base/Organization/FOO"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
}
}

View File

@ -0,0 +1,473 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
import ca.uhn.fhir.jpa.interceptor.JpaServerInterceptorAdapter;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4InterceptorTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4InterceptorTest.class);
private IJpaServerInterceptor myJpaInterceptor;
private JpaServerInterceptorAdapter myJpaInterceptorAdapter = new JpaServerInterceptorAdapter();
private IServerOperationInterceptor myServerOperationInterceptor;
@After
public void after() {
myDaoConfig.getInterceptors().remove(myJpaInterceptor);
myDaoConfig.getInterceptors().remove(myJpaInterceptorAdapter);
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
}
@Before
public void before() {
myJpaInterceptor = mock(IJpaServerInterceptor.class);
myServerOperationInterceptor = mock(IServerOperationInterceptor.class, new Answer<Object>() {
@Override
public Object answer(InvocationOnMock theInvocation) throws Throwable {
if (theInvocation.getMethod().getReturnType().equals(boolean.class)) {
return true;
}
return null;
}
});
myDaoConfig.getInterceptors().add(myJpaInterceptor);
myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter);
myDaoConfig.getInterceptors().add(myServerOperationInterceptor);
}
@Test
public void testJpaCreate() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
ArgumentCaptor<ActionRequestDetails> detailsCapt;
ArgumentCaptor<ResourceTable> tableCapt;
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
assertNotNull(tableCapt.getValue().getId());
assertEquals(id, tableCapt.getValue().getId());
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
/*
* Not do a conditional create
*/
p = new Patient();
p.addName().setFamily("PATIENT1");
Long id2 = myPatientDao.create(p, "Patient?family=PATIENT", mySrd).getId().getIdPartAsLong();
assertEquals(id, id2);
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
}
/*
* *****************************************************
* Note that non JPA specific operations get tested in individual
* operation test methods too
* *****************************************************
*/
@Test
public void testJpaDelete() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
myPatientDao.delete(new IdType("Patient", id), mySrd);
ArgumentCaptor<ActionRequestDetails> detailsCapt;
ArgumentCaptor<ResourceTable> tableCapt;
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture());
assertNotNull(tableCapt.getValue().getId());
assertEquals(id, tableCapt.getValue().getId());
}
@Test
public void testJpaUpdate() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
p = new Patient();
p.setId(new IdType(id));
p.addName().setFamily("PATIENT1");
Long id2 = myPatientDao.update(p, mySrd).getId().getIdPartAsLong();
assertEquals(id, id2);
ArgumentCaptor<ActionRequestDetails> detailsCapt;
ArgumentCaptor<ResourceTable> tableCapt;
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
assertNotNull(tableCapt.getValue().getId());
assertEquals(id, tableCapt.getValue().getId());
/*
* Now do a conditional update
*/
p = new Patient();
p.setId(new IdType(id));
p.addName().setFamily("PATIENT2");
id2 = myPatientDao.update(p, "Patient?family=PATIENT1", mySrd).getId().getIdPartAsLong();
assertEquals(id, id2);
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
assertEquals(id, tableCapt.getAllValues().get(2).getId());
/*
* Now do a conditional update where none will match (so this is actually a create)
*/
p = new Patient();
p.addName().setFamily("PATIENT3");
id2 = myPatientDao.update(p, "Patient?family=ZZZ", mySrd).getId().getIdPartAsLong();
assertNotEquals(id, id2);
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
verify(myJpaInterceptor, times(2)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
assertEquals(id2, tableCapt.getAllValues().get(3).getId());
}
@Test
public void testRequestOperationCreate() {
IServerOperationInterceptor interceptor = mock(IServerOperationInterceptor.class);
myServerInterceptorList.add(interceptor);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
Patient p = new Patient();
p.addName().setFamily("PATIENT");
IIdType id = myPatientDao.create(p, mySrd).getId();
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testServerOperationCreate() {
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
Patient p = new Patient();
p.addName().setFamily("PATIENT");
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
}
@SuppressWarnings("deprecation")
@Test
public void testServerOperationUpdate() {
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(0)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
Patient p = new Patient();
p.addName().setFamily("PATIENT");
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
p.addName().setFamily("2");
myPatientDao.update(p);
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(1)).resourceUpdated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class), any(IBaseResource.class));
}
@Test
public void testServerOperationDelete() {
verify(myServerOperationInterceptor, times(0)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(0)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
Patient p = new Patient();
p.addName().setFamily("PATIENT");
IIdType id = myPatientDao.create(p, (RequestDetails)null).getId();
assertEquals(1L, id.getVersionIdPartAsLong().longValue());
p.addName().setFamily("2");
myPatientDao.delete(p.getIdElement().toUnqualifiedVersionless());
verify(myServerOperationInterceptor, times(1)).resourceCreated(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
verify(myServerOperationInterceptor, times(1)).resourceDeleted(Mockito.isNull(RequestDetails.class), any(IBaseResource.class));
}
@Test
public void testRequestOperationDelete() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
IIdType newId = myPatientDao.delete(new IdType("Patient/" + id), mySrd).getId();
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationDeleteMulti() {
myDaoConfig.setAllowMultipleDelete(true);
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
p = new Patient();
p.addName().setFamily("PATIENT");
Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
DeleteMethodOutcome outcome = myPatientDao.deleteByUrl("Patient?name=PATIENT", mySrd);
String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
ourLog.info(oo);
assertThat(oo, containsString("deleted 2 resource(s)"));
verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationTransactionCreate() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceCreated(any(IBaseResource.class));
Bundle xactBundle = new Bundle();
xactBundle.setType(BundleType.TRANSACTION);
xactBundle
.addEntry()
.setResource(p)
.getRequest()
.setUrl("Patient")
.setMethod(HTTPVerb.POST);
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
assertEquals(1L, newId.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationTransactionDelete() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
Bundle xactBundle = new Bundle();
xactBundle.setType(BundleType.TRANSACTION);
xactBundle
.addEntry()
.getRequest()
.setUrl("Patient/" + id)
.setMethod(HTTPVerb.DELETE);
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceDeleted(any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationTransactionDeleteMulti() {
myDaoConfig.setAllowMultipleDelete(true);
Patient p = new Patient();
p.addName().setFamily("PATIENT");
Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
p = new Patient();
p.addName().setFamily("PATIENT");
Long id2 = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
Long id = res.getIdElement().getIdPartAsLong();
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceDeleted(any(IBaseResource.class));
Bundle xactBundle = new Bundle();
xactBundle.setType(BundleType.TRANSACTION);
xactBundle
.addEntry()
.getRequest()
.setUrl("Patient?name=PATIENT")
.setMethod(HTTPVerb.DELETE);
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
String oo = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp);
ourLog.info(oo);
assertThat(oo, containsString("deleted 2 resource(s)"));
verify(myRequestOperationCallback, times(2)).resourceDeleted(any(IBaseResource.class));
verify(myRequestOperationCallback, times(2)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationTransactionUpdate() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
p = new Patient();
p.setId(new IdType("Patient/" + id));
p.addName().setFamily("PATIENT2");
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[1];
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
Bundle xactBundle = new Bundle();
xactBundle.setType(BundleType.TRANSACTION);
xactBundle
.addEntry()
.setResource(p)
.getRequest()
.setUrl("Patient/" + id)
.setMethod(HTTPVerb.PUT);
Bundle resp = mySystemDao.transaction(mySrd, xactBundle);
IdType newId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@Test
public void testRequestOperationUpdate() {
Patient p = new Patient();
p.addName().setFamily("PATIENT");
final Long id = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource res = (IBaseResource) theInvocation.getArguments()[0];
assertEquals("Patient/" + id + "/_history/1", res.getIdElement().getValue());
res = (IBaseResource) theInvocation.getArguments()[1];
assertEquals("Patient/" + id + "/_history/2", res.getIdElement().getValue());
return null;
}}).when(myRequestOperationCallback).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
p = new Patient();
p.setId(new IdType("Patient/" + id));
p.addName().setFamily("PATIENT2");
IIdType newId = myPatientDao.update(p, mySrd).getId();
assertEquals(2L, newId.getVersionIdPartAsLong().longValue());
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceUpdated(any(IBaseResource.class), any(IBaseResource.class));
verify(myRequestOperationCallback, times(1)).resourceCreated(any(IBaseResource.class));
verifyNoMoreInteractions(myRequestOperationCallback);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,99 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4ReferentialIntegrityTest extends BaseJpaR4Test {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@After
public void afterResetConfig() {
myDaoConfig.setEnforceReferentialIntegrityOnWrite(new DaoConfig().isEnforceReferentialIntegrityOnWrite());
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
}
@Test
public void testCreateUnknownReferenceFail() throws Exception {
Patient p = new Patient();
p.setManagingOrganization(new Reference("Organization/AAA"));
try {
myPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
assertEquals("Resource Organization/AAA not found, specified in path: Patient.managingOrganization", e.getMessage());
}
}
@Test
public void testCreateUnknownReferenceAllow() throws Exception {
myDaoConfig.setEnforceReferentialIntegrityOnWrite(false);
Patient p = new Patient();
p.setManagingOrganization(new Reference("Organization/AAA"));
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
p = myPatientDao.read(id);
assertEquals("Organization/AAA", p.getManagingOrganization().getReference());
}
@Test
public void testDeleteFail() throws Exception {
Organization o = new Organization();
o.setName("FOO");
IIdType oid = myOrganizationDao.create(o).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.setManagingOrganization(new Reference(oid));
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
try {
myOrganizationDao.delete(oid);
fail();
} catch (ResourceVersionConflictException e) {
assertEquals("Unable to delete Organization/"+oid.getIdPart()+" because at least one resource has a reference to this resource. First reference found was resource Organization/"+oid.getIdPart()+" in path Patient.managingOrganization", e.getMessage());
}
myPatientDao.delete(pid);
myOrganizationDao.delete(oid);
}
@Test
public void testDeleteAllow() throws Exception {
myDaoConfig.setEnforceReferentialIntegrityOnDelete(false);
Organization o = new Organization();
o.setName("FOO");
IIdType oid = myOrganizationDao.create(o).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.setManagingOrganization(new Reference(oid));
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
myOrganizationDao.delete(oid);
myPatientDao.delete(pid);
}
}

View File

@ -0,0 +1,883 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.List;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Appointment.AppointmentStatus;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchCustomSearchParamTest.class);
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@Test
public void testCreateInvalidParamInvalidResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("PatientFoo.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid SearchParameter.expression value \"PatientFoo.gender\": Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"R4\")", e.getMessage());
}
}
@Test
public void testCreateInvalidNoBase() {
SearchParameter fooSp = new SearchParameter();
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("SearchParameter.base is missing", e.getMessage());
}
}
@Test
public void testCreateInvalidParamMismatchedResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender or Observation.code");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid SearchParameter.expression value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
}
}
@Test
public void testCreateInvalidParamNoPath() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("SearchParameter.expression is missing", e.getMessage());
}
}
@Test
public void testCreateInvalidParamNoResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
}
}
@Test
public void testCreateInvalidParamParamNullStatus() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(null);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@Test
public void testExtensionWithNoValueIndexesWithoutFailure() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(eyeColourSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").addExtension().setUrl("http://foo").setValue(new StringType("VAL"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
}
@Test
public void testSearchForExtensionToken() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(eyeColourSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.setActive(true);
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
// Try with custom gender SP
SearchParameterMap map = new SearchParameterMap();
map.add("eyecolour", new TokenParam(null, "blue"));
IBundleProvider results = myPatientDao.search(map);
List<String> foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p1id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepCoding() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Organization"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new Coding().setSystem("foo").setCode("bar"));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new TokenParam("foo", "bar"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepCodeableConcept() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Organization"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new CodeableConcept().addCoding(new Coding().setSystem("foo").setCode("bar")));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new TokenParam("foo", "bar"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepReferenceWrongType() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Observation"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Appointment apt = new Appointment();
apt.setStatus(AppointmentStatus.ARRIVED);
IIdType aptId = myAppointmentDao.create(apt).getId().toUnqualifiedVersionless();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new Reference(aptId.getValue()));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new ReferenceParam(aptId.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
}
@Test
public void testSearchForExtensionTwoDeepReferenceWithoutType() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Appointment apt = new Appointment();
apt.setStatus(AppointmentStatus.ARRIVED);
IIdType aptId = myAppointmentDao.create(apt).getId().toUnqualifiedVersionless();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new Reference(aptId.getValue()));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new ReferenceParam(aptId.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepReference() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Appointment"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Appointment apt = new Appointment();
apt.setStatus(AppointmentStatus.ARRIVED);
IIdType aptId = myAppointmentDao.create(apt).getId().toUnqualifiedVersionless();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new Reference(aptId.getValue()));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new ReferenceParam(aptId.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepDate() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.DATE);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Appointment apt = new Appointment();
apt.setStatus(AppointmentStatus.ARRIVED);
IIdType aptId = myAppointmentDao.create(apt).getId().toUnqualifiedVersionless();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new DateType("2012-01-02"));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new DateParam("2012-01-02"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepNumber() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.NUMBER);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new IntegerType(5));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new NumberParam("5"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepDecimal() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.NUMBER);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new DecimalType("2.1"));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new NumberParam("2.1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionTwoDeepString() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("foobar");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.STRING);
siblingSp.setTitle("FooBar");
siblingSp.setExpression("Patient.extension('http://acme.org/foo').extension('http://acme.org/bar')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient patient = new Patient();
patient.addName().setFamily("P2");
Extension extParent = patient
.addExtension()
.setUrl("http://acme.org/foo");
extParent
.addExtension()
.setUrl("http://acme.org/bar")
.setValue(new StringType("HELLOHELLO"));
IIdType p2id = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.add("foobar", new StringParam("hello"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
}
@Test
public void testSearchForExtensionReferenceWithNonMatchingTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("sibling");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("Sibling");
siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Organization"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
}
@Test
public void testSearchForExtensionReferenceWithoutTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("sibling");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("Sibling");
siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Appointment app = new Appointment();
app.addParticipant().getActor().setReference(p2id.getValue());
IIdType appid = myAppointmentDao.create(app).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by two level chain
map = new SearchParameterMap();
map.add("patient", new ReferenceParam("sibling.name", "P1"));
results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, containsInAnyOrder(appid.getValue()));
}
@Test
public void testSearchForExtensionReferenceWithTarget() {
SearchParameter siblingSp = new SearchParameter();
siblingSp.addBase("Patient");
siblingSp.setCode("sibling");
siblingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
siblingSp.setTitle("Sibling");
siblingSp.setExpression("Patient.extension('http://acme.org/sibling')");
siblingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
siblingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
siblingSp.getTarget().add(new CodeType("Patient"));
mySearchParameterDao.create(siblingSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.addName().setFamily("P1");
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/sibling").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Appointment app = new Appointment();
app.addParticipant().getActor().setReference(p2id.getValue());
IIdType appid = myAppointmentDao.create(app).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Search by ref
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam(p1id.getValue()));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by chain
map = new SearchParameterMap();
map.add("sibling", new ReferenceParam("name", "P1"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p2id.getValue()));
// Search by two level chain
map = new SearchParameterMap();
map.add("patient", new ReferenceParam("sibling.name", "P1"));
results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, containsInAnyOrder(appid.getValue()));
}
@Test
public void testIncludeExtensionReferenceAsRecurse() {
SearchParameter attendingSp = new SearchParameter();
attendingSp.addBase("Patient");
attendingSp.setCode("attending");
attendingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
attendingSp.setTitle("Attending");
attendingSp.setExpression("Patient.extension('http://acme.org/attending')");
attendingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
attendingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
attendingSp.getTarget().add(new CodeType("Practitioner"));
IIdType spId = mySearchParameterDao.create(attendingSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
Practitioner p1 = new Practitioner();
p1.addName().setFamily("P1");
IIdType p1id = myPractitionerDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/attending").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Appointment app = new Appointment();
app.addParticipant().getActor().setReference(p2id.getValue());
IIdType appId = myAppointmentDao.create(app).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
map = new SearchParameterMap();
map.addInclude(new Include("Appointment:patient", true));
map.addInclude(new Include("Patient:attending", true));
results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(appId.getValue(), p2id.getValue(), p1id.getValue()));
}
@Test
public void testSearchWithCustomParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
Patient pat = new Patient();
pat.setGender(AdministrativeGender.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Patient pat2 = new Patient();
pat.setGender(AdministrativeGender.FEMALE);
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Try with custom gender SP
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
// Try with normal gender SP
map = new SearchParameterMap();
map.add("gender", new TokenParam(null, "male"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
// Delete the param
mySearchParameterDao.delete(spId, mySrd);
mySearchParamRegsitry.forceRefresh();
mySystemDao.performReindexingPass(100);
// Try with custom gender SP
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
try {
myPatientDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter foo for resource type Patient", e.getMessage());
}
}
@Test
public void testSearchWithCustomParamDraft() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.DRAFT);
mySearchParameterDao.create(fooSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient pat = new Patient();
pat.setGender(AdministrativeGender.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Patient pat2 = new Patient();
pat.setGender(AdministrativeGender.FEMALE);
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Try with custom gender SP (should find nothing)
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
try {
myPatientDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter foo for resource type Patient", e.getMessage());
}
// Try with normal gender SP
map = new SearchParameterMap();
map.add("gender", new TokenParam(null, "male"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
}
@Test
public void testCustomReferenceParameter() throws Exception {
SearchParameter sp = new SearchParameter();
sp.addBase("Patient");
sp.setCode("myDoctor");
sp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
sp.setTitle("My Doctor");
sp.setExpression("Patient.extension('http://fmcna.com/myDoctor')");
sp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
sp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(sp);
org.hl7.fhir.r4.model.Practitioner pract = new org.hl7.fhir.r4.model.Practitioner();
pract.setId("A");
pract.addName().setFamily("PRACT");
myPractitionerDao.update(pract);
Patient pat = myFhirCtx.newJsonParser().parseResource(Patient.class, loadClasspath("/r4_custom_resource_patient.json"));
IIdType pid = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add("myDoctor", new ReferenceParam("A"));
IBundleProvider outcome = myPatientDao.search(params);
List<String> ids = toUnqualifiedVersionlessIdValues(outcome);
ourLog.info("IDS: " + ids);
assertThat(ids, contains(pid.getValue()));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,565 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchFtTest.class);
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testCodeTextSearch() {
Observation obs1 = new Observation();
obs1.getCode().setText("Systolic Blood Pressure");
obs1.setStatus(ObservationStatus.FINAL);
obs1.setValue(new Quantity(123));
obs1.setComment("obs1");
IIdType id1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.getCode().setText("Diastolic Blood Pressure");
obs2.setStatus(ObservationStatus.FINAL);
obs2.setValue(new Quantity(81));
IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Observation.SP_CODE, new TokenParam(null, "systolic").setModifier(TokenParamModifier.TEXT));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1)));
// map = new SearchParameterMap();
// map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
//
// map = new SearchParameterMap();
// map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
// assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
//
// map = new SearchParameterMap();
// map.add(Observation.SP_CODE, new TokenParam(null, "blood").setModifier(TokenParamModifier.TEXT));
// map.add(Constants.PARAM_CONTENT, new StringParam("obs1"));
// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1)));
}
@Test
public void testResourceTextSearch() {
Observation obs1 = new Observation();
obs1.getCode().setText("Systolic Blood Pressure");
obs1.setStatus(ObservationStatus.FINAL);
obs1.setValue(new Quantity(123));
obs1.setComment("obs1");
IIdType id1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.getCode().setText("Diastolic Blood Pressure");
obs2.setStatus(ObservationStatus.FINAL);
obs2.setValue(new Quantity(81));
IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("systolic"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("blood"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("obs1"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1)));
}
private ServletRequestDetails mockSrd() {
return mySrd;
}
@Test
@Ignore
public void testStringTextSearch() {
Observation obs1 = new Observation();
obs1.getCode().setText("AAAAA");
obs1.setValue(new StringType("Systolic Blood Pressure"));
obs1.setStatus(ObservationStatus.FINAL);
IIdType id1 = myObservationDao.create(obs1, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs1.getCode().setText("AAAAA");
obs1.setValue(new StringType("Diastolic Blood Pressure"));
obs2.setStatus(ObservationStatus.FINAL);
IIdType id2 = myObservationDao.create(obs2, mockSrd()).getId().toUnqualifiedVersionless();
SearchParameterMap map;
map = new SearchParameterMap();
map.add(Observation.SP_VALUE_STRING, new StringParam("sure").setContains(true));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
}
@Test
public void testSuggestIgnoresBase64Content() {
Patient patient = new Patient();
patient.addName().setFamily("testSuggest");
IIdType ptId = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
Media med = new Media();
med.getSubject().setReferenceElement(ptId);
med.getSubtype().setText("Systolic Blood Pressure");
med.getContent().setContentType("LCws");
med.getContent().setDataElement(new Base64BinaryType(new byte[] {44,44,44,44,44,44,44,44}));
med.getContent().setTitle("bbbb syst");
myMediaDao.create(med, mockSrd());
ourLog.info(myFhirCtx.newJsonParser().encodeResourceToString(med));
List<Suggestion> output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "press");
ourLog.info("Found: " + output);
assertEquals(2, output.size());
assertEquals("Pressure", output.get(0).getTerm());
assertEquals("Systolic Blood Pressure", output.get(1).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "prezure");
ourLog.info("Found: " + output);
assertEquals(2, output.size());
assertEquals("Pressure", output.get(0).getTerm());
assertEquals("Systolic Blood Pressure", output.get(1).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "syst");
ourLog.info("Found: " + output);
assertEquals(4, output.size());
assertEquals("syst", output.get(0).getTerm());
assertEquals("bbbb syst", output.get(1).getTerm());
assertEquals("Systolic", output.get(2).getTerm());
assertEquals("Systolic Blood Pressure", output.get(3).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "LCws");
ourLog.info("Found: " + output);
assertEquals(0, output.size());
}
@Test
public void testSuggest() {
Patient patient = new Patient();
patient.addName().setFamily("testSuggest");
IIdType ptId = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReferenceElement(ptId);
obs.getCode().setText("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL");
myObservationDao.create(obs, mockSrd());
obs = new Observation();
obs.getSubject().setReferenceElement(ptId);
obs.getCode().setText("MNBVCXZ");
myObservationDao.create(obs, mockSrd());
obs = new Observation();
obs.getSubject().setReferenceElement(ptId);
obs.getCode().setText("ZXC HELLO");
obs.addComponent().getCode().setText("HHHHHHHHHH");
myObservationDao.create(obs, mockSrd());
/*
* These shouldn't match since they're for another patient
*/
patient = new Patient();
patient.addName().setFamily("testSuggest2");
IIdType ptId2 = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.getSubject().setReferenceElement(ptId2);
obs2.getCode().setText("ZXCVBNMZZ");
myObservationDao.create(obs2, mockSrd());
List<Suggestion> output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "ZXCVBNM");
ourLog.info("Found: " + output);
assertEquals(4, output.size());
assertEquals("ZXCVBNM", output.get(0).getTerm());
assertEquals("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL", output.get(1).getTerm());
assertEquals("ZXC", output.get(2).getTerm());
assertEquals("ZXC HELLO", output.get(3).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "ZXC");
ourLog.info("Found: " + output);
assertEquals(4, output.size());
assertEquals("ZXC", output.get(0).getTerm());
assertEquals("ZXC HELLO", output.get(1).getTerm());
assertEquals("ZXCVBNM", output.get(2).getTerm());
assertEquals("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL", output.get(3).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "HELO");
ourLog.info("Found: " + output);
assertEquals(2, output.size());
assertEquals("HELLO", output.get(0).getTerm());
assertEquals("ZXC HELLO", output.get(1).getTerm());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "Z");
ourLog.info("Found: " + output);
assertEquals(0, output.size());
output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "ZX");
ourLog.info("Found: " + output);
assertEquals(2, output.size());
assertEquals("ZXC", output.get(0).getTerm());
assertEquals("ZXC HELLO", output.get(1).getTerm());
}
@Test
public void testSearchAndReindex() {
Patient patient;
SearchParameterMap map;
patient = new Patient();
patient.getText().setDivAsString("<div>DIVAAA</div>");
patient.addName().addGiven("NAMEAAA");
IIdType pId1 = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_TEXT, new StringParam("DIVAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
/*
* Reindex
*/
patient = new Patient();
patient.setId(pId1);
patient.getText().setDivAsString("<div>DIVBBB</div>");
patient.addName().addGiven("NAMEBBB");
myPatientDao.update(patient, mockSrd());
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
map = new SearchParameterMap();
map.add(Patient.SP_NAME, new StringParam("NAMEBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_TEXT, new StringParam("DIVBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
}
@Test
public void testEverythingInstanceWithContentFilter() {
Patient pt1 = new Patient();
pt1.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId1 = myPatientDao.create(pt1, mockSrd()).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId2 = myPatientDao.create(pt2, mockSrd()).getId().toUnqualifiedVersionless();
Device dev1 = new Device();
dev1.setManufacturer("Some Manufacturer");
IIdType devId1 = myDeviceDao.create(dev1, mockSrd()).getId().toUnqualifiedVersionless();
Device dev2 = new Device();
dev2.setManufacturer("Some Manufacturer 2");
myDeviceDao.create(dev2, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs1 = new Observation();
obs1.getText().setDivAsString("<div>OBSTEXT1</div>");
obs1.getSubject().setReferenceElement(ptId1);
obs1.getCode().addCoding().setCode("CODE1");
obs1.setValue(new StringType("obsvalue1"));
obs1.getDevice().setReferenceElement(devId1);
IIdType obsId1 = myObservationDao.create(obs1, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.getSubject().setReferenceElement(ptId1);
obs2.getCode().addCoding().setCode("CODE2");
obs2.setValue(new StringType("obsvalue2"));
IIdType obsId2 = myObservationDao.create(obs2, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs3 = new Observation();
obs3.getSubject().setReferenceElement(ptId2);
obs3.getCode().addCoding().setCode("CODE3");
obs3.setValue(new StringType("obsvalue3"));
IIdType obsId3 = myObservationDao.create(obs3, mockSrd()).getId().toUnqualifiedVersionless();
HttpServletRequest request;
List<String> actual;
request = mock(HttpServletRequest.class);
StringAndListParam param;
ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", new Object[] {ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart()});
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1)));
/*
* Add another match
*/
Observation obs4 = new Observation();
obs4.getSubject().setReferenceElement(ptId1);
obs4.getCode().addCoding().setCode("CODE1");
obs4.setValue(new StringType("obsvalue1"));
IIdType obsId4 = myObservationDao.create(obs4, mockSrd()).getId().toUnqualifiedVersionless();
assertNotEquals(obsId1.getIdPart(), obsId4.getIdPart(), devId1);
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
* Make one previous match no longer match
*/
obs1 = new Observation();
obs1.setId(obsId1);
obs1.getSubject().setReferenceElement(ptId1);
obs1.getCode().addCoding().setCode("CODE2");
obs1.setValue(new StringType("obsvalue2"));
myObservationDao.update(obs1, mockSrd());
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}
@Test
public void testEverythingTypeWithContentFilter() {
Patient pt1 = new Patient();
pt1.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId1 = myPatientDao.create(pt1, mockSrd()).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId2 = myPatientDao.create(pt2, mockSrd()).getId().toUnqualifiedVersionless();
Device dev1 = new Device();
dev1.setManufacturer("Some Manufacturer");
IIdType devId1 = myDeviceDao.create(dev1, mockSrd()).getId().toUnqualifiedVersionless();
Device dev2 = new Device();
dev2.setManufacturer("Some Manufacturer 2");
myDeviceDao.create(dev2, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs1 = new Observation();
obs1.getSubject().setReferenceElement(ptId1);
obs1.getCode().addCoding().setCode("CODE1");
obs1.setValue(new StringType("obsvalue1"));
obs1.getDevice().setReferenceElement(devId1);
IIdType obsId1 = myObservationDao.create(obs1, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.getSubject().setReferenceElement(ptId1);
obs2.getCode().addCoding().setCode("CODE2");
obs2.setValue(new StringType("obsvalue2"));
IIdType obsId2 = myObservationDao.create(obs2, mockSrd()).getId().toUnqualifiedVersionless();
Observation obs3 = new Observation();
obs3.getSubject().setReferenceElement(ptId2);
obs3.getCode().addCoding().setCode("CODE3");
obs3.setValue(new StringType("obsvalue3"));
IIdType obsId3 = myObservationDao.create(obs3, mockSrd()).getId().toUnqualifiedVersionless();
HttpServletRequest request;
List<String> actual;
request = mock(HttpServletRequest.class);
StringAndListParam param;
ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", new Object[] {ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart()});
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1, ptId2, obsId3)));
/*
* Add another match
*/
Observation obs4 = new Observation();
obs4.getSubject().setReferenceElement(ptId1);
obs4.getCode().addCoding().setCode("CODE1");
obs4.setValue(new StringType("obsvalue1"));
IIdType obsId4 = myObservationDao.create(obs4, mockSrd()).getId().toUnqualifiedVersionless();
assertNotEquals(obsId1.getIdPart(), obsId4.getIdPart(), devId1);
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
* Make one previous match no longer match
*/
obs1 = new Observation();
obs1.setId(obsId1);
obs1.getSubject().setReferenceElement(ptId1);
obs1.getCode().addCoding().setCode("CODE2");
obs1.setValue(new StringType("obsvalue2"));
myObservationDao.update(obs1, mockSrd());
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}
/**
* When processing transactions, we do two passes. Make sure we don't update the lucene index twice since that would
* be inefficient
*/
@Test
public void testSearchDontReindexForUpdateWithIndexDisabled() {
Patient patient;
SearchParameterMap map;
patient = new Patient();
patient.getText().setDivAsString("<div>DIVAAA</div>");
patient.addName().addGiven("NAMEAAA");
IIdType pId1 = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_TEXT, new StringParam("DIVAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
/*
* Update but don't reindex
*/
patient = new Patient();
patient.setId(pId1);
patient.getText().setDivAsString("<div>DIVBBB</div>");
patient.addName().addGiven("NAMEBBB");
myPatientDao.update(patient, null, false, mockSrd());
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), not(contains(toValues(pId1))));
myPatientDao.update(patient, null, true, mockSrd());
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
map = new SearchParameterMap();
map.add(Patient.SP_NAME, new StringParam("NAMEBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
map = new SearchParameterMap();
map.add(Constants.PARAM_TEXT, new StringParam("DIVBBB"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(pId1)));
}
@Test
public void testSearchWithChainedParams() {
String methodName = "testSearchWithChainedParams";
IIdType pId1;
{
Patient patient = new Patient();
patient.addName().addGiven(methodName);
patient.addAddress().addLine("My fulltext address");
pId1 = myPatientDao.create(patient, mockSrd()).getId().toUnqualifiedVersionless();
}
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId1);
obs.setValue(new StringType("This is the FULLtext of the observation"));
IIdType oId1 = myObservationDao.create(obs, mockSrd()).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId1);
obs.setValue(new StringType("Another fullText"));
IIdType oId2 = myObservationDao.create(obs, mockSrd()).getId().toUnqualifiedVersionless();
List<String> patients;
SearchParameterMap params;
params = new SearchParameterMap();
params.add(Constants.PARAM_CONTENT, new StringParam("fulltext"));
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
assertThat(patients, containsInAnyOrder(toValues(pId1)));
params = new SearchParameterMap();
params.add(Constants.PARAM_CONTENT, new StringParam("FULLTEXT"));
patients = toUnqualifiedVersionlessIdValues(myObservationDao.search(params));
assertThat(patients, containsInAnyOrder(toValues(oId1, oId2)));
}
}

View File

@ -0,0 +1,174 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@After()
public void after() {
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
}
@Before
public void before() {
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
}
@Test
public void testExpirePagesAfterSingleUse() throws Exception {
IIdType pid1;
IIdType pid2;
{
Patient patient = new Patient();
patient.addName().setFamily("EXPIRE");
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Thread.sleep(10);
{
Patient patient = new Patient();
patient.addName().setFamily("EXPIRE");
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Thread.sleep(10);
SearchParameterMap params;
params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
final IBundleProvider bundleProvider = myPatientDao.search(params);
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
myDaoConfig.setExpireSearchResultsAfterMillis(500);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNotNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
}
});
Thread.sleep(750);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNull(mySearchEntityDao.findByUuid(bundleProvider.getUuid()));
}
});
}
@Test
public void testExpirePagesAfterReuse() throws Exception {
IIdType pid1;
IIdType pid2;
{
Patient patient = new Patient();
patient.addName().setFamily("EXPIRE");
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Thread.sleep(10);
{
Patient patient = new Patient();
patient.addName().setFamily("EXPIRE");
pid2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Thread.sleep(10);
myDaoConfig.setExpireSearchResultsAfterMillis(1000L);
myDaoConfig.setReuseCachedSearchResultsForMillis(500L);
final String searchUuid1;
{
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
final IBundleProvider bundleProvider = myPatientDao.search(params);
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
searchUuid1 = bundleProvider.getUuid();
Validate.notBlank(searchUuid1);
}
Thread.sleep(250);
String searchUuid2;
{
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
final IBundleProvider bundleProvider = myPatientDao.search(params);
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
searchUuid2 = bundleProvider.getUuid();
Validate.notBlank(searchUuid2);
}
assertEquals(searchUuid1, searchUuid2);
Thread.sleep(500);
// We're now past 500ms so we shouldn't reuse the search
final String searchUuid3;
{
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringParam("EXPIRE"));
final IBundleProvider bundleProvider = myPatientDao.search(params);
assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2));
searchUuid3 = bundleProvider.getUuid();
Validate.notBlank(searchUuid3);
}
assertNotEquals(searchUuid1, searchUuid3);
// Search just got used so it shouldn't be deleted
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNotNull(mySearchEntityDao.findByUuid(searchUuid1));
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
}
});
Thread.sleep(750);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
}
});
Thread.sleep(300);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
assertNull(mySearchEntityDao.findByUuid(searchUuid3));
}
});
}
}

View File

@ -0,0 +1,225 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import javax.persistence.EntityManager;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.TestR4WithoutLuceneConfig;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
// @RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration(classes= {TestR4WithoutLuceneConfig.class})
// @SuppressWarnings("unchecked")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestR4WithoutLuceneConfig.class })
public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchWithLuceneDisabledTest.class);
@Autowired
@Qualifier("myAllergyIntoleranceDaoR4")
private IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
@Autowired
@Qualifier("myAppointmentDaoR4")
private IFhirResourceDao<Appointment> myAppointmentDao;
@Autowired
@Qualifier("myAuditEventDaoR4")
private IFhirResourceDao<AuditEvent> myAuditEventDao;
@Autowired
@Qualifier("myBundleDaoR4")
private IFhirResourceDao<Bundle> myBundleDao;
@Autowired
@Qualifier("myCarePlanDaoR4")
private IFhirResourceDao<CarePlan> myCarePlanDao;
@Autowired
@Qualifier("myCodeSystemDaoR4")
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
@Autowired
@Qualifier("myCompartmentDefinitionDaoR4")
private IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@Qualifier("myConceptMapDaoR4")
private IFhirResourceDao<ConceptMap> myConceptMapDao;
@Autowired
@Qualifier("myConditionDaoR4")
private IFhirResourceDao<Condition> myConditionDao;
@Autowired
protected DaoConfig myDaoConfig;
@Autowired
@Qualifier("myDeviceDaoR4")
private IFhirResourceDao<Device> myDeviceDao;
@Autowired
@Qualifier("myDiagnosticReportDaoR4")
private IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
@Autowired
@Qualifier("myEncounterDaoR4")
private IFhirResourceDao<Encounter> myEncounterDao;
// @PersistenceContext()
@Autowired
private EntityManager myEntityManager;
@Autowired
private FhirContext myFhirCtx;
@Autowired
@Qualifier("myImmunizationDaoR4")
private IFhirResourceDao<Immunization> myImmunizationDao;
@Autowired
@Qualifier("myLocationDaoR4")
private IFhirResourceDao<Location> myLocationDao;
@Autowired
@Qualifier("myMediaDaoR4")
private IFhirResourceDao<Media> myMediaDao;
@Autowired
@Qualifier("myMedicationDaoR4")
private IFhirResourceDao<Medication> myMedicationDao;
@Autowired
@Qualifier("myMedicationRequestDaoR4")
private IFhirResourceDao<MedicationRequest> myMedicationRequestDao;
@Autowired
@Qualifier("myNamingSystemDaoR4")
private IFhirResourceDao<NamingSystem> myNamingSystemDao;
@Autowired
@Qualifier("myObservationDaoR4")
private IFhirResourceDao<Observation> myObservationDao;
@Autowired
@Qualifier("myOperationDefinitionDaoR4")
private IFhirResourceDao<OperationDefinition> myOperationDefinitionDao;
@Autowired
@Qualifier("myOrganizationDaoR4")
private IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
@Qualifier("myPatientDaoR4")
private IFhirResourceDaoPatient<Patient> myPatientDao;
@Autowired
@Qualifier("myPractitionerDaoR4")
private IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired
@Qualifier("myQuestionnaireDaoR4")
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
@Autowired
@Qualifier("myQuestionnaireResponseDaoR4")
private IFhirResourceDao<QuestionnaireResponse> myQuestionnaireResponseDao;
@Autowired
@Qualifier("myResourceProvidersR4")
private Object myResourceProviders;
@Autowired
@Qualifier("myStructureDefinitionDaoR4")
private IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
@Autowired
@Qualifier("mySubscriptionDaoR4")
private IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
@Autowired
@Qualifier("mySubstanceDaoR4")
private IFhirResourceDao<Substance> mySubstanceDao;
@Autowired
@Qualifier("mySystemDaoR4")
private IFhirSystemDao<Bundle, Meta> mySystemDao;
@Autowired
@Qualifier("mySystemProviderR4")
private JpaSystemProviderR4 mySystemProvider;
@Autowired
protected PlatformTransactionManager myTxManager;
@Autowired
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Autowired
@Qualifier("myJpaValidationSupportChainR4")
private IValidationSupport myValidationSupport;
@Autowired
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
@Before
@Transactional()
public void beforePurgeDatabase() {
final EntityManager entityManager = this.myEntityManager;
purgeDatabase(entityManager, myTxManager, mySearchParamPresenceSvc, mySearchCoordinatorSvc);
}
@Before
public void beforeResetConfig() {
myDaoConfig.setHardSearchLimit(1000);
myDaoConfig.setHardTagListLimit(1000);
myDaoConfig.setIncludeLimit(2000);
}
@Override
protected FhirContext getContext() {
return myFhirCtx;
}
@Test
public void testSearchWithRegularParam() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(Organization.SP_NAME, new StringParam(methodName));
myOrganizationDao.search(map);
}
@Test
public void testSearchWithContent() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, new StringParam(methodName));
try {
myOrganizationDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Fulltext search is not enabled on this service, can not process parameter: _content", e.getMessage());
}
}
@Test
public void testSearchWithText() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, new StringParam(methodName));
try {
myOrganizationDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Fulltext search is not enabled on this service, can not process parameter: _text", e.getMessage());
}
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,530 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Date;
import java.util.List;
import javax.persistence.TypedQuery;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.data.ISubscriptionFlaggedResourceDataDao;
import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4SubscriptionTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SubscriptionTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Autowired
private ISubscriptionFlaggedResourceDataDao mySubscriptionFlaggedResourceDataDao;
@Autowired
private ISubscriptionTableDao mySubscriptionTableDao;
@Before
public void beforeEnableSubscription() {
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPurgeInactiveAfterSeconds(60);
}
@Test
public void testSubscriptionGetsPurgedIfItIsNeverActive() throws Exception {
myDaoConfig.setSubscriptionPurgeInactiveAfterSeconds(1);
Subscription subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setStatus(SubscriptionStatus.REQUESTED);
IIdType id = mySubscriptionDao.create(subs, mySrd).getId().toUnqualifiedVersionless();
mySubscriptionDao.purgeInactiveSubscriptions();
mySubscriptionDao.read(id, mySrd);
Thread.sleep(1500);
myDaoConfig.setSchedulingDisabled(false);
mySubscriptionDao.purgeInactiveSubscriptions();
try {
mySubscriptionDao.read(id, mySrd);
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Before
public void beforeDisableScheduling() {
myDaoConfig.setSchedulingDisabled(true);
}
@Test
public void testSubscriptionGetsPurgedIfItIsInactive() throws Exception {
myDaoConfig.setSubscriptionPurgeInactiveAfterSeconds(1);
Subscription subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setStatus(SubscriptionStatus.REQUESTED);
IIdType id = mySubscriptionDao.create(subs, mySrd).getId().toUnqualifiedVersionless();
mySubscriptionDao.purgeInactiveSubscriptions();
mySubscriptionDao.read(id, mySrd);
mySubscriptionDao.getUndeliveredResourcesAndPurge(mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id));
Thread.sleep(1500);
myDaoConfig.setSchedulingDisabled(false);
mySubscriptionDao.purgeInactiveSubscriptions();
try {
mySubscriptionDao.read(id, mySrd);
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Test
public void testCreateSubscription() {
Subscription subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setStatus(SubscriptionStatus.REQUESTED);
IIdType id = mySubscriptionDao.create(subs, mySrd).getId().toUnqualifiedVersionless();
TypedQuery<SubscriptionTable> q = myEntityManager.createQuery("SELECT t from SubscriptionTable t WHERE t.mySubscriptionResource.myId = :id", SubscriptionTable.class);
q.setParameter("id", id.getIdPartAsLong());
final SubscriptionTable table = q.getSingleResult();
assertNotNull(table);
assertNotNull(table.getNextCheck());
assertEquals(table.getNextCheck(), table.getSubscriptionResource().getPublished().getValue());
assertEquals(SubscriptionStatus.REQUESTED.toCode(), myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
assertEquals(SubscriptionStatus.REQUESTED, mySubscriptionDao.read(id, mySrd).getStatusElement().getValue());
subs.setStatus(SubscriptionStatus.ACTIVE);
mySubscriptionDao.update(subs, mySrd);
assertEquals(SubscriptionStatus.ACTIVE.toCode(), myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
assertEquals(SubscriptionStatus.ACTIVE, mySubscriptionDao.read(id, mySrd).getStatusElement().getValue());
mySubscriptionDao.delete(id, mySrd);
assertNull(myEntityManager.find(SubscriptionTable.class, table.getId()));
/*
* Re-create again
*/
subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setId(id);
subs.setStatus(SubscriptionStatus.REQUESTED);
mySubscriptionDao.update(subs, mySrd);
assertEquals(SubscriptionStatus.REQUESTED.toCode(), myEntityManager.createQuery("SELECT t FROM SubscriptionTable t WHERE t.myResId = " + id.getIdPart(), SubscriptionTable.class).getSingleResult().getStatus());
assertEquals(SubscriptionStatus.REQUESTED, mySubscriptionDao.read(id, mySrd).getStatusElement().getValue());
}
@Test
public void testCreateSubscriptionInvalidCriteria() {
Subscription subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("http://foo.com/Observation?AAA=BBB");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("ObservationZZZZ?a=b");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria contains invalid/unsupported resource type: ObservationZZZZ"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.channel.type must be populated"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.channel.payload must be populated for rest-hook subscriptions"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("text/html");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Invalid value for Subscription.channel.payload: text/html"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("application/fhir+xml");
try {
mySubscriptionDao.create(subs, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Rest-hook subscriptions must have Subscription.channel.endpoint defined"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
assertTrue(mySubscriptionDao.create(subs, mySrd).getId().hasIdPart());
subs = new Subscription();
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("application/fhir+json");
subs.getChannel().setEndpoint("http://localhost:8080");
assertTrue(mySubscriptionDao.create(subs, mySrd).getId().hasIdPart());
}
@Test
public void testDeleteSubscriptionWithFlaggedResources() throws Exception {
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testDeleteSubscriptionWithFlaggedResources";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Subscription subs;
/*
* Create 2 identical subscriptions
*/
subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatus.ACTIVE);
IIdType subsId = mySubscriptionDao.create(subs, mySrd).getId().toUnqualifiedVersionless();
Long subsPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(subsId);
assertNull(mySubscriptionTableDao.findOne(subsPid).getLastClientPoll());
Thread.sleep(100);
ourLog.info("Before: {}", System.currentTimeMillis());
assertThat(mySubscriptionFlaggedResourceDataDao.count(), not(greaterThan(0L)));
assertThat(mySubscriptionTableDao.count(), equalTo(1L));
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("After: {}", System.currentTimeMillis());
mySubscriptionDao.pollForNewUndeliveredResources();
assertThat(mySubscriptionFlaggedResourceDataDao.count(), greaterThan(0L));
assertThat(mySubscriptionTableDao.count(), greaterThan(0L));
/*
* Delete the subscription
*/
mySubscriptionDao.delete(subsId, mySrd);
assertThat(mySubscriptionFlaggedResourceDataDao.count(), not(greaterThan(0L)));
assertThat(mySubscriptionTableDao.count(), not(greaterThan(0L)));
/*
* Delete a second time just to make sure that works
*/
mySubscriptionDao.delete(subsId, mySrd);
/*
* Re-create the subscription
*/
subs.setId(subsId);
mySubscriptionDao.update(subs, mySrd).getId();
assertThat(mySubscriptionFlaggedResourceDataDao.count(), not(greaterThan(0L)));
assertThat(mySubscriptionTableDao.count(), (greaterThan(0L)));
/*
* Create another resource and make sure it gets flagged
*/
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertThat(mySubscriptionFlaggedResourceDataDao.count(), greaterThan(0L));
assertThat(mySubscriptionTableDao.count(), greaterThan(0L));
}
@Test
public void testSubscriptionResourcesAppear() throws Exception {
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionResourcesAppear";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Subscription subs;
/*
* Create 2 identical subscriptions
*/
subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatus.ACTIVE);
Long subsId1 = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(mySubscriptionDao.create(subs, mySrd).getId());
subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatus.ACTIVE);
Long subsId2 = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(mySubscriptionDao.create(subs, mySrd).getId());
assertNull(mySubscriptionTableDao.findOne(subsId1).getLastClientPoll());
Thread.sleep(100);
ourLog.info("Before: {}", System.currentTimeMillis());
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("After: {}", System.currentTimeMillis());
List<IBaseResource> results;
List<IIdType> resultIds;
assertEquals(4, mySubscriptionDao.pollForNewUndeliveredResources());
assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources());
results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subsId1);
resultIds = toUnqualifiedVersionlessIds(results);
assertThat(resultIds, contains(afterId1, afterId2));
Date lastClientPoll = mySubscriptionTableDao.findOne(subsId1).getLastClientPoll();
assertNotNull(lastClientPoll);
mySubscriptionDao.pollForNewUndeliveredResources();
results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subsId2);
resultIds = toUnqualifiedVersionlessIds(results);
assertThat(resultIds, contains(afterId1, afterId2));
mySubscriptionDao.pollForNewUndeliveredResources();
results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subsId1);
resultIds = toUnqualifiedVersionlessIds(results);
assertThat(resultIds, empty());
assertNotEquals(lastClientPoll, mySubscriptionTableDao.findOne(subsId1).getLastClientPoll());
mySubscriptionDao.pollForNewUndeliveredResources();
results = mySubscriptionDao.getUndeliveredResourcesAndPurge(subsId2);
resultIds = toUnqualifiedVersionlessIds(results);
assertThat(resultIds, empty());
/*
* Make sure that reindexing doesn't trigger
*/
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(100);
assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources());
/*
* Update resources on disk
*/
IBundleProvider allObs = myObservationDao.search(new SearchParameterMap());
ourLog.info("Updating {} observations", allObs.size());
for (IBaseResource next : allObs.getResources(0, allObs.size())) {
ourLog.info("Updating observation");
Observation nextObs = (Observation) next;
nextObs.addPerformer().setDisplay("Some display");
myObservationDao.update(nextObs, mySrd);
}
assertEquals(6, mySubscriptionDao.pollForNewUndeliveredResources());
assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources());
}
@Test
public void testSubscriptionResourcesAppear2() throws Exception {
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionResourcesAppear2";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType oId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Subscription subs;
/*
* Create 2 identical subscriptions
*/
subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatus.ACTIVE);
Long subsId1 = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(mySubscriptionDao.create(subs, mySrd).getId());
assertNull(mySubscriptionTableDao.findOne(subsId1).getLastClientPoll());
assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources());
ourLog.info("pId: {} - oId: {}", pId, oId);
myObservationDao.update(myObservationDao.read(oId, mySrd), mySrd);
assertEquals(1, mySubscriptionDao.pollForNewUndeliveredResources());
ourLog.info("Between passes");
assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources());
Thread.sleep(100);
ourLog.info("Before: {}", System.currentTimeMillis());
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("After: {}", System.currentTimeMillis());
assertEquals(2, mySubscriptionDao.pollForNewUndeliveredResources());
assertEquals(3, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(3, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(3, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(4, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(4, mySubscriptionFlaggedResourceDataDao.count());
}
}

View File

@ -0,0 +1,868 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import java.util.*;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UpdateTest.class);
@Test
public void testCreateAndUpdateWithoutRequest() throws Exception {
String methodName = "testUpdateByUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName + "2");
IIdType id = myPatientDao.create(p).getId().toUnqualified();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName + "2");
p.setActive(true);
IIdType id2 = myPatientDao.create(p, "Patient?identifier=urn:system|" + methodName + "2").getId().toUnqualified();
assertEquals(id.getValue(), id2.getValue());
p = new Patient();
p.setId(id);
p.addIdentifier().setSystem("urn:system").setValue(methodName + "2");
p.setActive(false);
myPatientDao.update(p).getId();
p.setActive(true);
id2 = myPatientDao.update(p, "Patient?identifier=urn:system|" + methodName + "2").getId().toUnqualified();
assertEquals(id.getIdPart(), id2.getIdPart());
assertEquals("3", id2.getVersionIdPart());
Patient newPatient = myPatientDao.read(id);
assertEquals("1", newPatient.getIdElement().getVersionIdPart());
newPatient = myPatientDao.read(id.toVersionless());
assertEquals("3", newPatient.getIdElement().getVersionIdPart());
myPatientDao.delete(id.toVersionless());
try {
myPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// nothing
}
}
@Test
public void testDuplicateProfilesIgnored() {
String name = "testDuplicateProfilesIgnored";
IIdType id;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
List<IdType> tl = new ArrayList<IdType>();
tl.add(new IdType("http://foo/bar"));
tl.add(new IdType("http://foo/bar"));
tl.add(new IdType("http://foo/bar"));
patient.getMeta().getProfile().addAll(tl);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
// Do a read
{
Patient patient = myPatientDao.read(id, mySrd);
List<UriType> tl = patient.getMeta().getProfile();
assertEquals(1, tl.size());
assertEquals("http://foo/bar", tl.get(0).getValue());
}
}
@After
public void afterResetDao() {
myDaoConfig.setResourceMetaCountHardLimit(new DaoConfig().getResourceMetaCountHardLimit());
}
@Test
public void testHardMetaCapIsEnforcedOnCreate() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.getMeta().addTag().setSystem("http://foo").setCode("1");
patient.getMeta().addTag().setSystem("http://foo").setCode("2");
patient.getMeta().addTag().setSystem("http://foo").setCode("3");
patient.getMeta().addTag().setSystem("http://foo").setCode("4");
patient.setActive(true);
try {
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test
public void testHardMetaCapIsEnforcedOnMetaAdd() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.setActive(true);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Meta meta = new Meta();
meta.addTag().setSystem("http://foo").setCode("1");
meta.addTag().setSystem("http://foo").setCode("2");
meta.addTag().setSystem("http://foo").setCode("3");
meta.addTag().setSystem("http://foo").setCode("4");
try {
myPatientDao.metaAddOperation(id, meta, null);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test
public void testDuplicateTagsOnAddTagsIgnored() {
IIdType id;
{
Patient patient = new Patient();
patient.setActive(true);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Meta meta = new Meta();
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val1");
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val2");
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val3");
myPatientDao.metaAddOperation(id, meta, null);
// Do a read
{
Patient patient = myPatientDao.read(id, mySrd);
List<Coding> tl = patient.getMeta().getTag();
assertEquals(1, tl.size());
assertEquals("http://foo", tl.get(0).getSystem());
assertEquals("bar", tl.get(0).getCode());
}
}
@Test
public void testDuplicateTagsOnUpdateIgnored() {
IIdType id;
{
Patient patient = new Patient();
patient.setActive(true);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Patient patient = new Patient();
patient.setId(id);
patient.setActive(true);
patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val1");
patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val2");
patient.getMeta().addTag().setSystem("http://foo").setCode("bar").setDisplay("Val3");
myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
}
// Do a read on second version
{
Patient patient = myPatientDao.read(id, mySrd);
List<Coding> tl = patient.getMeta().getTag();
assertEquals(1, tl.size());
assertEquals("http://foo", tl.get(0).getSystem());
assertEquals("bar", tl.get(0).getCode());
}
// Do a read on first version
{
Patient patient = myPatientDao.read(id.withVersion("1"), mySrd);
List<Coding> tl = patient.getMeta().getTag();
assertEquals(0, tl.size());
}
Meta meta = new Meta();
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val1");
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val2");
meta.addTag().setSystem("http://foo").setCode("bar").setDisplay("Val3");
myPatientDao.metaAddOperation(id.withVersion("1"), meta, null);
// Do a read on first version
{
Patient patient = myPatientDao.read(id.withVersion("1"), mySrd);
List<Coding> tl = patient.getMeta().getTag();
assertEquals(1, tl.size());
assertEquals("http://foo", tl.get(0).getSystem());
assertEquals("bar", tl.get(0).getCode());
}
}
@Test
public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForDiscreteUpdates() {
// First time
Patient p = new Patient();
p.setActive(true);
p.setId("Patient/A");
String id = myPatientDao.update(p).getId().getValue();
assertThat(id, endsWith("Patient/A/_history/1"));
// Second time should not result in an update
p = new Patient();
p.setActive(true);
p.setId("Patient/A");
id = myPatientDao.update(p).getId().getValue();
assertThat(id, endsWith("Patient/A/_history/1"));
// And third time should not result in an update
p = new Patient();
p.setActive(true);
p.setId("Patient/A");
id = myPatientDao.update(p).getId().getValue();
assertThat(id, endsWith("Patient/A/_history/1"));
myPatientDao.read(new IdType("Patient/A"));
myPatientDao.read(new IdType("Patient/A/_history/1"));
try {
myPatientDao.read(new IdType("Patient/A/_history/2"));
fail();
} catch (ResourceNotFoundException e) {
// good
}
try {
myPatientDao.read(new IdType("Patient/A/_history/3"));
fail();
} catch (ResourceNotFoundException e) {
// good
}
// Create one more
p = new Patient();
p.setActive(false);
p.setId("Patient/A");
id = myPatientDao.update(p).getId().getValue();
assertThat(id, endsWith("Patient/A/_history/2"));
}
@Test
public void testUpdateAndGetHistoryResource() throws InterruptedException {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
MethodOutcome outcome = myPatientDao.create(patient, mySrd);
assertNotNull(outcome.getId());
assertFalse(outcome.getId().isEmpty());
assertEquals("1", outcome.getId().getVersionIdPart());
Date now = new Date();
Patient retrieved = myPatientDao.read(outcome.getId(), mySrd);
InstantType updated = retrieved.getMeta().getLastUpdatedElement().copy();
assertTrue(updated.before(now));
Thread.sleep(1000);
reset(myInterceptor);
retrieved.getIdentifier().get(0).setValue("002");
MethodOutcome outcome2 = myPatientDao.update(retrieved, mySrd);
assertEquals(outcome.getId().getIdPart(), outcome2.getId().getIdPart());
assertNotEquals(outcome.getId().getVersionIdPart(), outcome2.getId().getVersionIdPart());
assertEquals("2", outcome2.getId().getVersionIdPart());
// Verify interceptor
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertNotNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());
Date now2 = new Date();
Patient retrieved2 = myPatientDao.read(outcome.getId().toVersionless(), mySrd);
assertEquals("2", retrieved2.getIdElement().getVersionIdPart());
assertEquals("002", retrieved2.getIdentifier().get(0).getValue());
InstantType updated2 = retrieved2.getMeta().getLastUpdatedElement();
assertTrue(updated2.after(now));
assertTrue(updated2.before(now2));
Thread.sleep(2000);
/*
* Get history
*/
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd);
assertEquals(2, historyBundle.size().intValue());
List<IBaseResource> history = historyBundle.getResources(0, 2);
ourLog.info("updated : {}", updated.getValueAsString());
ourLog.info(" * Exp : {}", ((Resource) history.get(1)).getMeta().getLastUpdatedElement().getValueAsString());
ourLog.info("updated2: {}", updated2.getValueAsString());
ourLog.info(" * Exp : {}", ((Resource) history.get(0)).getMeta().getLastUpdatedElement().getValueAsString());
assertEquals("1", history.get(1).getIdElement().getVersionIdPart());
assertEquals("2", history.get(0).getIdElement().getVersionIdPart());
assertEquals(updated.getValueAsString(), ((Resource) history.get(1)).getMeta().getLastUpdatedElement().getValueAsString());
assertEquals("001", ((Patient) history.get(1)).getIdentifier().get(0).getValue());
assertEquals(updated2.getValueAsString(), ((Resource) history.get(0)).getMeta().getLastUpdatedElement().getValueAsString());
assertEquals("002", ((Patient) history.get(0)).getIdentifier().get(0).getValue());
}
@Test
public void testUpdateByUrl() {
String methodName = "testUpdateByUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IIdType id = myPatientDao.create(p, mySrd).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().setFamily("Hello");
p.setId("Patient/" + methodName);
myPatientDao.update(p, "Patient?identifier=urn%3Asystem%7C" + methodName, mySrd);
p = myPatientDao.read(id.toVersionless(), mySrd);
assertThat(p.getIdElement().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getIdElement().toVersionless());
assertNotEquals(id, p.getIdElement());
assertThat(p.getIdElement().toString(), endsWith("/_history/2"));
}
@Test
public void testUpdateConditionalByLastUpdated() throws Exception {
String methodName = "testUpdateByUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName + "2");
myPatientDao.create(p, mySrd).getId();
InstantDt start = InstantDt.withCurrentTime();
ourLog.info("First time: {}", start.getValueAsString());
Thread.sleep(100);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IIdType id = myPatientDao.create(p, mySrd).getId();
ourLog.info("Created patient, got ID: {}", id);
Thread.sleep(100);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().setFamily("Hello");
p.setId("Patient/" + methodName);
String matchUrl = "Patient?_lastUpdated=gt" + start.getValueAsString();
ourLog.info("URL is: {}", matchUrl);
myPatientDao.update(p, matchUrl, mySrd);
p = myPatientDao.read(id.toVersionless(), mySrd);
assertThat(p.getIdElement().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getIdElement().toVersionless());
assertNotEquals(id, p.getIdElement());
assertThat(p.getIdElement().toString(), endsWith("/_history/2"));
}
@Test
public void testUpdateConditionalByLastUpdatedWithWrongTimezone() throws Exception {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("GMT-0:00"));
String methodName = "testUpdateByUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName + "2");
myPatientDao.create(p, mySrd).getId();
InstantDt start = InstantDt.withCurrentTime();
Thread.sleep(100);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IIdType id = myPatientDao.create(p, mySrd).getId();
ourLog.info("Created patient, got it: {}", id);
Thread.sleep(100);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().setFamily("Hello");
p.setId("Patient/" + methodName);
myPatientDao.update(p, "Patient?_lastUpdated=gt" + start.getValueAsString(), mySrd);
p = myPatientDao.read(id.toVersionless(), mySrd);
assertThat(p.getIdElement().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getIdElement().toVersionless());
assertNotEquals(id, p.getIdElement());
assertThat(p.getIdElement().toString(), endsWith("/_history/2"));
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testUpdateCreatesTextualIdIfItDoesntAlreadyExist() {
Patient p = new Patient();
String methodName = "testUpdateCreatesTextualIdIfItDoesntAlreadyExist";
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().setFamily("Hello");
p.setId("Patient/" + methodName);
IIdType id = myPatientDao.update(p, mySrd).getId();
assertEquals("Patient/" + methodName, id.toUnqualifiedVersionless().getValue());
p = myPatientDao.read(id, mySrd);
assertEquals(methodName, p.getIdentifier().get(0).getValue());
}
@Test
public void testUpdateDoesntFailForUnknownIdWithNumberThenText() {
String methodName = "testUpdateFailsForUnknownIdWithNumberThenText";
Patient p = new Patient();
p.setId("0" + methodName);
p.addName().setFamily(methodName);
myPatientDao.update(p, mySrd);
}
@Test
@Ignore
public void testUpdateIgnoresIdenticalVersions() {
String methodName = "testUpdateIgnoresIdenticalVersions";
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue(methodName);
p1.addName().setFamily("Tester").addGiven(methodName);
IIdType p1id = myPatientDao.create(p1, mySrd).getId();
IIdType p1id2 = myPatientDao.update(p1, mySrd).getId();
assertEquals(p1id.getValue(), p1id2.getValue());
p1.addName().addGiven("NewGiven");
IIdType p1id3 = myPatientDao.update(p1, mySrd).getId();
assertNotEquals(p1id.getValue(), p1id3.getValue());
}
@Test
public void testUpdateMaintainsSearchParams() {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2AAA");
p1.addName().setFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2AAA");
IIdType p1id = myPatientDao.create(p1, mySrd).getId();
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testUpdateMaintainsSearchParamsDstu2BBB");
p2.addName().setFamily("Tester").addGiven("testUpdateMaintainsSearchParamsDstu2BBB");
myPatientDao.create(p2, mySrd).getId();
Set<Long> ids = myPatientDao.searchForIds(new SearchParameterMap(Patient.SP_GIVEN, new StringParam("testUpdateMaintainsSearchParamsDstu2AAA")));
assertEquals(1, ids.size());
assertThat(ids, contains(p1id.getIdPartAsLong()));
// Update the name
p1.getName().get(0).getGiven().get(0).setValue("testUpdateMaintainsSearchParamsDstu2BBB");
MethodOutcome update2 = myPatientDao.update(p1, mySrd);
IIdType p1id2 = update2.getId();
ids = myPatientDao.searchForIds(new SearchParameterMap(Patient.SP_GIVEN, new StringParam("testUpdateMaintainsSearchParamsDstu2AAA")));
assertEquals(0, ids.size());
ids = myPatientDao.searchForIds(new SearchParameterMap(Patient.SP_GIVEN, new StringParam("testUpdateMaintainsSearchParamsDstu2BBB")));
assertEquals(2, ids.size());
// Make sure vreads work
p1 = myPatientDao.read(p1id, mySrd);
assertEquals("testUpdateMaintainsSearchParamsDstu2AAA", p1.getName().get(0).getGivenAsSingleString());
p1 = myPatientDao.read(p1id2, mySrd);
assertEquals("testUpdateMaintainsSearchParamsDstu2BBB", p1.getName().get(0).getGivenAsSingleString());
}
/**
* Per the spec, update should preserve tags and security labels but not profiles
*/
@Test
public void testUpdateMaintainsTagsAndSecurityLabels() {
String methodName = "testUpdateMaintainsTagsAndSecurityLabels";
IIdType p1id;
{
Patient p1 = new Patient();
p1.addName().setFamily(methodName);
p1.getMeta().addTag("tag_scheme1", "tag_term1", null);
p1.getMeta().addSecurity("sec_scheme1", "sec_term1", null);
p1.getMeta().addProfile("http://foo1");
p1id = myPatientDao.create(p1, mySrd).getId().toUnqualifiedVersionless();
}
{
Patient p1 = new Patient();
p1.setId(p1id);
p1.addName().setFamily(methodName);
p1.getMeta().addTag("tag_scheme2", "tag_term2", null);
p1.getMeta().addSecurity("sec_scheme2", "sec_term2", null);
p1.getMeta().addProfile("http://foo2");
myPatientDao.update(p1, mySrd);
}
{
Patient p1 = myPatientDao.read(p1id, mySrd);
List<Coding> tagList = p1.getMeta().getTag();
Set<String> secListValues = new HashSet<String>();
for (Coding next : tagList) {
secListValues.add(next.getSystemElement().getValue() + "|" + next.getCodeElement().getValue());
}
assertThat(secListValues, containsInAnyOrder("tag_scheme1|tag_term1", "tag_scheme2|tag_term2"));
List<Coding> secList = p1.getMeta().getSecurity();
secListValues = new HashSet<String>();
for (Coding next : secList) {
secListValues.add(next.getSystemElement().getValue() + "|" + next.getCodeElement().getValue());
}
assertThat(secListValues, containsInAnyOrder("sec_scheme1|sec_term1", "sec_scheme2|sec_term2"));
List<UriType> profileList = p1.getMeta().getProfile();
assertEquals(1, profileList.size());
assertEquals("http://foo2", profileList.get(0).getValueAsString()); // no foo1
}
}
@Test
public void testUpdateModifiesProfiles() {
String name = "testUpdateModifiesProfiles";
IIdType id;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
List<IdType> tl = new ArrayList<IdType>();
tl.add(new IdType("http://foo/bar"));
patient.getMeta().getProfile().addAll(tl);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
// Do a read
{
Patient patient = myPatientDao.read(id, mySrd);
List<UriType> tl = patient.getMeta().getProfile();
assertEquals(1, tl.size());
assertEquals("http://foo/bar", tl.get(0).getValue());
}
// Update
{
Patient patient = new Patient();
patient.setId(id);
patient.addName().setFamily(name);
List<IdType> tl = new ArrayList<IdType>();
tl.add(new IdType("http://foo/baz"));
patient.getMeta().getProfile().clear();
patient.getMeta().getProfile().addAll(tl);
id = myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
}
// Do a read
{
Patient patient = myPatientDao.read(id, mySrd);
List<UriType> tl = patient.getMeta().getProfile();
assertEquals(1, tl.size());
assertEquals("http://foo/baz", tl.get(0).getValue());
}
}
@Test
public void testUpdateRejectsInvalidTypes() {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateRejectsInvalidTypes");
p1.addName().setFamily("Tester").addGiven("testUpdateRejectsInvalidTypes");
IIdType p1id = myPatientDao.create(p1, mySrd).getId();
Organization p2 = new Organization();
p2.getNameElement().setValue("testUpdateRejectsInvalidTypes");
try {
p2.setId(new IdType("Organization/" + p1id.getIdPart()));
myOrganizationDao.update(p2, mySrd);
fail();
} catch (UnprocessableEntityException e) {
// good
}
try {
p2.setId(new IdType("Patient/" + p1id.getIdPart()));
myOrganizationDao.update(p2, mySrd);
fail();
} catch (UnprocessableEntityException e) {
ourLog.error("Good", e);
}
}
@Test
public void testUpdateUnknownNumericIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().setFamily("Hello");
p.setId("Patient/9999999999999999");
try {
myPatientDao.update(p, mySrd);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Can not create resource with ID[9999999999999999], no resource with this ID exists and clients may only"));
}
}
@Test
public void testUpdateWithInvalidIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().setFamily("Hello");
p.setId("Patient/123:456");
try {
myPatientDao.update(p, mySrd);
fail();
} catch (InvalidRequestException e) {
assertEquals("Can not process entity with ID[123:456], this is not a valid FHIR ID", e.getMessage());
}
}
@Test
public void testUpdateWithNoChangeDetectionDisabledUpdateUnchanged() {
myDaoConfig.setSuppressUpdatesWithNoChange(false);
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
// Update
{
Patient patient = new Patient();
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertNotEquals(id1.getValue(), id2.getValue());
}
@Test
public void testUpdateWithNoChangeDetectionUpdateTagAdded() {
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
// Update
{
Patient patient = new Patient();
patient.getMeta().addTag().setCode("CODE");
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertNotEquals(id1.getValue(), id2.getValue());
}
@Test
public void testUpdateWithNoChangeDetectionUpdateTagMetaRemoved() {
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.getMeta().addTag().setCode("CODE");
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
Meta meta = new Meta();
meta.addTag().setCode("CODE");
myPatientDao.metaDeleteOperation(id1, meta, null);
meta = myPatientDao.metaGetOperation(Meta.class, id1, null);
assertEquals(0, meta.getTag().size());
// Update
{
Patient patient = new Patient();
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertEquals(id1.getValue(), id2.getValue());
meta = myPatientDao.metaGetOperation(Meta.class, id2, null);
assertEquals(0, meta.getTag().size());
}
@Test
public void testUpdateWithNoChangeDetectionUpdateTagNoChange() {
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
// Add tag
Meta meta = new Meta();
meta.addTag().setCode("CODE");
myPatientDao.metaAddOperation(id1, meta, null);
// Update with tag
{
Patient patient = new Patient();
patient.getMeta().addTag().setCode("CODE");
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertEquals(id1.getValue(), id2.getValue());
meta = myPatientDao.metaGetOperation(Meta.class, id2, null);
assertEquals(1, meta.getTag().size());
assertEquals("CODE", meta.getTag().get(0).getCode());
}
@Test
public void testUpdateWithNoChangeDetectionUpdateTagRemoved() {
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.getMeta().addTag().setCode("CODE");
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
// Update
{
Patient patient = new Patient();
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertEquals(id1.getValue(), id2.getValue());
Meta meta = myPatientDao.metaGetOperation(Meta.class, id2, null);
assertEquals(1, meta.getTag().size());
assertEquals("CODE", meta.getTag().get(0).getCode());
}
@Test
public void testUpdateWithNoChangeDetectionUpdateUnchanged() {
String name = "testUpdateUnchanged";
IIdType id1, id2;
{
Patient patient = new Patient();
patient.addName().setFamily(name);
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualified();
}
// Update
{
Patient patient = new Patient();
patient.setId(id1);
patient.addName().setFamily(name);
id2 = myPatientDao.update(patient, mySrd).getId().toUnqualified();
}
assertEquals(id1.getValue(), id2.getValue());
}
@Test
public void testUpdateWithNumericIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().setFamily("Hello");
p.setId("Patient/123");
try {
myPatientDao.update(p, mySrd);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("clients may only assign IDs which contain at least one non-numeric"));
}
}
@Test
public void testUpdateWithNumericThenTextIdSucceeds() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().setFamily("Hello");
p.setId("Patient/123abc");
IIdType id = myPatientDao.update(p, mySrd).getId();
assertEquals("123abc", id.getIdPart());
assertEquals("1", id.getVersionIdPart());
p = myPatientDao.read(id.toUnqualifiedVersionless(), mySrd);
assertEquals("Patient/123abc", p.getIdElement().toUnqualifiedVersionless().getValue());
assertEquals("Hello", p.getName().get(0).getFamily());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,303 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ValidateTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testValidateStructureDefinition() throws Exception {
String input = IOUtils.toString(getClass().getResourceAsStream("/sd-david-dhtest7.json"), StandardCharsets.UTF_8);
StructureDefinition sd = myFhirCtx.newJsonParser().parseResource(StructureDefinition.class, input);
ourLog.info("Starting validation");
try {
myStructureDefinitionDao.validate(sd, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
} catch (PreconditionFailedException e) {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
}
ourLog.info("Done validation");
StopWatch sw = new StopWatch();
ourLog.info("Starting validation");
try {
myStructureDefinitionDao.validate(sd, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
} catch (PreconditionFailedException e) {
// ok
}
ourLog.info("Done validation in {}ms", sw.getMillis());
}
@Test
public void testValidateDocument() throws Exception {
String input = IOUtils.toString(getClass().getResourceAsStream("/document-bundle-r4.json"), StandardCharsets.UTF_8);
Bundle document = myFhirCtx.newJsonParser().parseResource(Bundle.class, input);
ourLog.info("Starting validation");
try {
MethodOutcome outcome = myBundleDao.validate(document, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
} catch (PreconditionFailedException e) {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
}
ourLog.info("Done validation");
// ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome()));
}
@Test
@Ignore
public void testValidateResourceContainingProfileDeclarationJson() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationJson";
OperationOutcome outcome = doTestValidateResourceContainingProfileDeclaration(methodName, EncodingEnum.JSON);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Element '.subject': minimum required = 1, but only found 0"));
assertThat(ooString, containsString("Element encounter @ : max allowed = 0, but found 1"));
assertThat(ooString, containsString("Element '.device': minimum required = 1, but only found 0"));
}
@Test
@Ignore
public void testValidateResourceContainingProfileDeclarationXml() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationXml";
OperationOutcome outcome = doTestValidateResourceContainingProfileDeclaration(methodName, EncodingEnum.XML);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
assertThat(ooString, containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
assertThat(ooString, containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
}
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
Bundle vss = loadResourceFromClasspath(Bundle.class, "/org/hl7/fhir/r4/model/valueset/valuesets.xml");
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-status"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-category"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-codes"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-methods"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-valueabsentreason"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-interpretation"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "body-site"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "referencerange-meaning"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-relationshiptypes"), mySrd);
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/org/hl7/fhir/r4/model/profile/devicemetricobservation.profile.xml");
sd.setId(new IdType());
sd.setUrl("http://example.com/foo/bar/" + methodName);
myStructureDefinitionDao.create(sd, mySrd);
Observation input = new Observation();
input.getMeta().getProfile().add(new IdType(sd.getUrl()));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getContext().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
String encoded = null;
MethodOutcome outcome = null;
ValidationModeEnum mode = ValidationModeEnum.CREATE;
switch (enc) {
case JSON:
encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
try {
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
fail();
} catch (PreconditionFailedException e) {
return (OperationOutcome) e.getOperationOutcome();
}
case XML:
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
try {
myObservationDao.validate(input, null, encoded, EncodingEnum.XML, mode, null, mySrd);
fail();
} catch (PreconditionFailedException e) {
return (OperationOutcome) e.getOperationOutcome();
}
}
throw new IllegalStateException(); // shouldn't get here
}
@Test
public void testValidateResourceContainingProfileDeclarationInvalid() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationInvalid";
Observation input = new Observation();
String profileUri = "http://example.com/" + methodName;
input.getMeta().getProfile().add(new IdType(profileUri));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getContext().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatus.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
ValidationModeEnum mode = ValidationModeEnum.CREATE;
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
MethodOutcome outcome = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
ourLog.info(ooString);
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
}
@Test
public void testValidateForCreate() {
String methodName = "testValidateForCreate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().setFamily(methodName);
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must not be populated"));
}
pat.setId("");
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
}
@Test
public void testValidateForUpdate() {
String methodName = "testValidateForUpdate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().setFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForUpdateWithContained() {
String methodName = "testValidateForUpdate";
Organization org = new Organization();
org.setId("#123");
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().setFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForDelete() {
String methodName = "testValidateForDelete";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
Patient pat = new Patient();
pat.addName().setFamily(methodName);
pat.getManagingOrganization().setReference(orgId.getValue());
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
OperationOutcome outcome = null;
try {
myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null, mySrd);
fail();
} catch (ResourceVersionConflictException e) {
outcome = (OperationOutcome) e.getOperationOutcome();
}
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Unable to delete Organization"));
pat.setId(patId);
pat.getManagingOrganization().setReference("");
myPatientDao.update(pat, mySrd);
outcome = (OperationOutcome) myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null, mySrd).getOperationOutcome();
ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Ok to delete"));
}
private IBaseResource findResourceByIdInBundle(Bundle vss, String name) {
IBaseResource retVal = null;
for (BundleEntryComponent next : vss.getEntry()) {
if (next.getResource().getIdElement().getIdPart().equals(name)) {
retVal = next.getResource();
break;
}
}
if (retVal == null) {
fail("Can't find VS: " + name);
}
return retVal;
}
/**
* Format has changed, this is out of date
*/
@Test
@Ignore
public void testValidateNewQuestionnaireFormat() throws Exception {
String input =IOUtils.toString(FhirResourceDaoR4ValidateTest.class.getResourceAsStream("/questionnaire_r4.xml"));
try {
MethodOutcome results = myQuestionnaireDao.validate(null, null, input, EncodingEnum.XML, ValidationModeEnum.UPDATE, null, mySrd);
OperationOutcome oo = (OperationOutcome) results.getOperationOutcome();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
} catch (PreconditionFailedException e) {
// this is a failure of the test
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
throw e;
}
}
}

View File

@ -0,0 +1,242 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ValueSetTest.class);
private IIdType myExtensionalVsId;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@Transactional
public void before02() throws IOException {
ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
CodeSystem upload2 = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
myCodeSystemDao.create(upload2, mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testValidateCodeOperationByCodeAndSystemBad() {
UriType valueSetIdentifier = null;
IdType id = null;
CodeType code = new CodeType("8450-9-XXX");
UriType system = new UriType("http://acme.org");
StringType display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult());
}
@Test
public void testValidateCodeOperationByCodeAndSystemGood() {
UriType valueSetIdentifier = null;
IdType id = null;
CodeType code = new CodeType("8450-9");
UriType system = new UriType("http://acme.org");
StringType display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult());
assertEquals("Systolic blood pressure--expiration", result.getDisplay());
}
@Test
public void testValidateCodeOperationByIdentifierAndCodeAndSystem() {
UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2");
IdType id = null;
CodeType code = new CodeType("11378-7");
UriType system = new UriType("http://acme.org");
StringType display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
}
@Test
public void testValidateCodeOperationByIdentifierAndCodeAndSystemAndBadDisplay() {
UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2");
IdType id = null;
CodeType code = new CodeType("11378-7");
UriType system = new UriType("http://acme.org");
StringType display = new StringType("Systolic blood pressure at First encounterXXXX");
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
}
@Test
public void testValidateCodeOperationByIdentifierAndCodeAndSystemAndGoodDisplay() {
UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2");
IdType id = null;
CodeType code = new CodeType("11378-7");
UriType system = new UriType("http://acme.org");
StringType display = new StringType("Systolic blood pressure at First encounter");
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
}
@Test
public void testValidateCodeOperationByResourceIdAndCodeableConcept() {
UriType valueSetIdentifier = null;
IIdType id = myExtensionalVsId;
CodeType code = null;
UriType system = null;
StringType display = null;
Coding coding = null;
CodeableConcept codeableConcept = new CodeableConcept();
codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7");
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
}
@Test
public void testValidateCodeOperationByResourceIdAndCodeAndSystem() {
UriType valueSetIdentifier = null;
IIdType id = myExtensionalVsId;
CodeType code = new CodeType("11378-7");
UriType system = new UriType("http://acme.org");
StringType display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
}
@Test
public void testExpandById() throws IOException {
String resp;
ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
assertThat(resp, containsString("<expansion>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"8450-9\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure--expiration\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"11378-7\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("</expansion>"));
/*
* Filter with display name
*/
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
@Test
@Ignore
public void testExpandByIdentifier() {
ValueSet expanded = myValueSetDao.expandByIdentifier("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "11378");
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
/**
* This type of expansion doesn't really make sense..
*/
@Test
@Ignore
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
ValueSet expanded = myValueSetDao.expand(toExpand, "11378");
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
assertThat(resp, not(containsString("<code value=\"8450-9\"/>")));
}
@Test
public void testValiedateCodeAgainstBuiltInValueSetAndCodeSystemWithValidCode() {
IPrimitiveType<String> display = null;
Coding coding = null;
CodeableConcept codeableConcept = null;
StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/v2-0487");
StringType code = new StringType("BRN");
StringType system = new StringType("http://hl7.org/fhir/v2/0487");
ValidateCodeResult result = myValueSetDao.validateCode(vsIdentifier, null, code, system, display, coding, codeableConcept, mySrd);
ourLog.info(result.getMessage());
assertTrue(result.getMessage(), result.isResult());
}
}

View File

@ -0,0 +1,184 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertThat;
import java.util.List;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.util.TestUtil;
public class FhirSearchDaoR4Test extends BaseJpaR4Test {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Autowired
private IFulltextSearchSvc mySearchDao;
@Test
public void testContentSearch() {
Long id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addGiven("testSearchStringParamWithNonNormalized_h\u00F6ra");
patient.addName().addGiven("AAAS");
patient.addName().addGiven("CCC");
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
Long id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addGiven("testSearchStringParamWithNonNormalized_HORA");
patient.addName().addGiven("AAAB");
patient.addName().addGiven("CCC");
id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
Long id3;
{
Organization org = new Organization();
org.setName("DDD");
id3 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
SearchParameterMap map = new SearchParameterMap();
String resourceName = "Patient";
// One term
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")));
map.add(Constants.PARAM_CONTENT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1));
}
// OR
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")).addOr(new StringParam("AAAB")));
map.add(Constants.PARAM_CONTENT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1, id2));
}
// AND
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")));
content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")));
map.add(Constants.PARAM_CONTENT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1));
}
// AND OR
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAB")).addOr(new StringParam("AAAS")));
content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")));
map.add(Constants.PARAM_CONTENT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1, id2));
}
// All Resource Types
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")).addOr(new StringParam("DDD")));
map.add(Constants.PARAM_CONTENT, content);
List<Long> found = mySearchDao.search(null, map);
assertThat(found, containsInAnyOrder(id1, id2, id3));
}
}
@Test
public void testNarrativeSearch() {
Long id1;
{
Patient patient = new Patient();
patient.getText().setDivAsString("<div>AAAS<p>FOO</p> CCC </div>");
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
Long id2;
{
Patient patient = new Patient();
patient.getText().setDivAsString("<div>AAAB<p>FOO</p> CCC </div>");
id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
{
Patient patient = new Patient();
patient.getText().setDivAsString("<div>ZZYZXY</div>");
myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getIdPartAsLong();
}
SearchParameterMap map = new SearchParameterMap();
String resourceName = "Patient";
// One term
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")));
map.add(Constants.PARAM_TEXT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1));
}
// OR
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")).addOr(new StringParam("AAAB")));
map.add(Constants.PARAM_TEXT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1, id2));
}
// AND
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")));
content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")));
map.add(Constants.PARAM_TEXT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1));
}
// AND OR
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("AAAB")).addOr(new StringParam("AAAS")));
content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")));
map.add(Constants.PARAM_TEXT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, containsInAnyOrder(id1, id2));
}
// Tag Contents
{
StringAndListParam content = new StringAndListParam();
content.addAnd(new StringOrListParam().addOr(new StringParam("div")));
map.add(Constants.PARAM_TEXT, content);
List<Long> found = mySearchDao.search(resourceName, map);
assertThat(found, empty());
}
}
}

View File

@ -0,0 +1,52 @@
package ca.uhn.fhir.jpa.dao.r4;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.util.TestUtil;
public class FhirSystemDaoR4SearchTest extends BaseJpaR4SystemTest {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testSearchByParans() {
// code to come.. just here to prevent a failure
}
/*//@formatter:off
* [ERROR] Search parameter action has conflicting types token and reference
* [ERROR] Search parameter source has conflicting types token and reference
* [ERROR] Search parameter plan has conflicting types reference and token
* [ERROR] Search parameter version has conflicting types token and string
* [ERROR] Search parameter source has conflicting types reference and uri
* [ERROR] Search parameter location has conflicting types reference and uri
* [ERROR] Search parameter title has conflicting types string and token
* [ERROR] Search parameter manufacturer has conflicting types string and reference
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter source has conflicting types reference and string
* [ERROR] Search parameter destination has conflicting types reference and string
* [ERROR] Search parameter responsible has conflicting types reference and string
* [ERROR] Search parameter value has conflicting types token and string
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter action has conflicting types reference and token
* [ERROR] Search parameter version has conflicting types token and string
* [ERROR] Search parameter address has conflicting types token and string
* [ERROR] Search parameter base has conflicting types reference and token
* [ERROR] Search parameter target has conflicting types reference and token
* [ERROR] Search parameter base has conflicting types reference and uri
* [ERROR] Search parameter contact has conflicting types string and token
* [ERROR] Search parameter substance has conflicting types token and reference
* [ERROR] Search parameter provider has conflicting types reference and token
* [ERROR] Search parameter system has conflicting types token and uri
* [ERROR] Search parameter reference has conflicting types reference and uri
* //@formatter:off
*/
}

View File

@ -0,0 +1,73 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.Assert.assertEquals;
import java.util.*;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.Observation;
import org.junit.*;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.util.TestUtil;
public class SearchParamExtractorR4Test {
private static FhirContext ourCtx = FhirContext.forR4();
private static IValidationSupport ourValidationSupport;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourValidationSupport = new DefaultProfileValidationSupport();
}
@Test
public void testParamWithOrInPath() {
Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
ISearchParamRegistry searchParamRegistry = new ISearchParamRegistry() {
@Override
public Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
RuntimeResourceDefinition nextResDef = ourCtx.getResourceDefinition(theResourceName);
Map<String, RuntimeSearchParam> sps = new HashMap<String, RuntimeSearchParam>();
for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) {
sps.put(nextSp.getName(), nextSp);
}
return sps;
}
@Override
public void forceRefresh() {
// nothing
}
@Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
throw new UnsupportedOperationException();
}
@Override
public RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName) {
throw new UnsupportedOperationException();
}
};
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(ourCtx, ourValidationSupport, searchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
assertEquals("category", token.getParamName());
assertEquals("SYSTEM", token.getSystem());
assertEquals("CODE", token.getValue());
}
}

View File

@ -0,0 +1,302 @@
package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.rest.api.Constants;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.util.TestUtil;
public class AuthorizationInterceptorResourceProviderR4Test extends BaseResourceProviderR4Test {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Override
public void before() throws Exception {
super.before();
myDaoConfig.setAllowMultipleDelete(true);
unregisterInterceptors();
}
private void unregisterInterceptors() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourRestServer.getInterceptors())) {
if (next instanceof AuthorizationInterceptor) {
ourRestServer.unregisterInterceptor(next);
}
}
}
/**
* See #503
*/
@Test
public void testDeleteIsBlocked() {
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
IIdType id = ourClient.create().resource(patient).execute().getId();
try {
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
patient = ourClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
assertEquals(id.getValue(), patient.getId());
}
/**
* See #503
*/
@Test
public void testDeleteIsAllowedForCompartment() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final IIdType id = ourClient.create().resource(patient).execute().getId();
Observation obsInCompartment = new Observation();
obsInCompartment.setStatus(ObservationStatus.FINAL);
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
IIdType obsInCompartmentId = ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
Observation obsNotInCompartment = new Observation();
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
IIdType obsNotInCompartmentId = ourClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
try {
ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
}
@Test
public void testCreateConditional() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final MethodOutcome output1 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen()
.allow().updateConditional().allResources()
.build();
//@formatter:on
}
});
patient = new Patient();
patient.setId(output1.getId().toUnqualifiedVersionless());
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
patient = new Patient();
patient.setId("999");
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
ourClient.update().resource(patient).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
}
/**
* See #667
*/
@Test
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
Patient pt1 = new Patient();
pt1.setActive(true);
final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid1).andThen()
.build();
}
});
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid1));
IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified();
unregisterInterceptors();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid2).andThen()
.build();
}
});
/*
* Try to update to a new patient. The user has access to write to things in
* pid2's compartment, so this would normally be ok, but in this case they are overwriting
* a resource that is already in pid1's compartment, which shouldn't be allowed.
*/
obs = new Observation();
obs.setId(oid);
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid2));
try {
ourClient.update().resource(obs).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
}
@Test
public void testDeleteResourceConditional() throws IOException {
String methodName = "testDeleteResourceConditional";
Patient pt = new Patient();
pt.addName().setFamily(methodName);
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
final IdType id;
try {
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
id = new IdType(newIdString);
} finally {
response.close();
}
pt = new Patient();
pt.addName().setFamily("FOOFOOFOO");
resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
response = ourHttpClient.execute(post);
final IdType id2;
try {
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
id2 = new IdType(newIdString);
} finally {
response.close();
}
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 2").delete().allResources().inCompartment("Patient", new IdDt("Patient/" + id.getIdPart())).andThen()
.build();
//@formatter:on
}
});
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
response = ourHttpClient.execute(delete);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
delete = new HttpDelete(ourServerBase + "/Patient?name=FOOFOOFOO");
response = ourHttpClient.execute(delete);
try {
assertEquals(403, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
}
}

View File

@ -0,0 +1,198 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import java.util.concurrent.TimeUnit;
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.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.*;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.DispatcherServlet;
import ca.uhn.fhir.jpa.config.r4.WebsocketR4Config;
import ca.uhn.fhir.jpa.config.r4.WebsocketR4DispatcherConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4;
import ca.uhn.fhir.jpa.interceptor.r4.RestHookSubscriptionR4Interceptor;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
protected static JpaValidationSupportChainR4 myValidationSupport;
protected static IGenericClient ourClient;
protected static CloseableHttpClient ourHttpClient;
protected static int ourPort;
protected static RestfulServer ourRestServer;
private static Server ourServer;
protected static String ourServerBase;
private static GenericWebApplicationContext ourWebApplicationContext;
private TerminologyUploaderProviderR4 myTerminologyUploaderProvider;
protected static SearchParamRegistryR4 ourSearchParamRegistry;
protected static DatabaseBackedPagingProvider ourPagingProvider;
protected static RestHookSubscriptionR4Interceptor ourRestHookSubscriptionInterceptor;
protected static ISearchDao mySearchEntityDao;
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
public BaseResourceProviderR4Test() {
super();
}
@After
public void after() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Before
public void before() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
if (ourServer == null) {
ourPort = PortUtil.findFreePort();
ourRestServer = new RestfulServer(myFhirCtx);
ourServerBase = "http://localhost:" + ourPort + "/fhir/context";
ourRestServer.setResourceProviders((List) myResourceProviders);
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class);
ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider);
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(ourRestServer, mySystemDao, myDaoConfig);
confProvider.setImplementationDescription("THIS IS THE DESC");
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
Server server = new Server(ourPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourWebApplicationContext = new GenericWebApplicationContext();
ourWebApplicationContext.setParent(myAppCtx);
ourWebApplicationContext.refresh();
// ContextLoaderListener loaderListener = new ContextLoaderListener(webApplicationContext);
// loaderListener.initWebApplicationContext(mock(ServletContext.class));
//
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// dispatcherServlet.setApplicationContext(webApplicationContext);
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
ServletHolder subsServletHolder = new ServletHolder();
subsServletHolder.setServlet(dispatcherServlet);
subsServletHolder.setInitParameter(
ContextLoader.CONFIG_LOCATION_PARAM,
WebsocketR4Config.class.getName() + "\n" +
WebsocketR4DispatcherConfig.class.getName());
proxyHandler.addServlet(subsServletHolder, "/*");
// Register a CORS filter
CorsConfiguration config = new CorsConfiguration();
CorsInterceptor corsInterceptor = new CorsInterceptor(config);
config.addAllowedHeader("x-fhir-starter");
config.addAllowedHeader("Origin");
config.addAllowedHeader("Accept");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedHeader("Access-Control-Request-Method");
config.addAllowedHeader("Access-Control-Request-Headers");
config.addAllowedOrigin("*");
config.addExposedHeader("Location");
config.addExposedHeader("Content-Location");
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
ourRestServer.registerInterceptor(corsInterceptor);
server.setHandler(proxyHandler);
server.start();
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(JpaValidationSupportChainR4.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
mySearchEntityDao = wac.getBean(ISearchDao.class);
ourRestHookSubscriptionInterceptor = wac.getBean(RestHookSubscriptionR4Interceptor.class);
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR4.class);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
if (shouldLogClient()) {
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
builder.setMaxConnPerRoute(99);
ourHttpClient = builder.build();
ourServer = server;
}
ourRestServer.setPagingProvider(ourPagingProvider);
}
protected boolean shouldLogClient() {
return true;
}
protected List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<String>();
for (BundleEntryComponent next : resp.getEntry()) {
Patient nextPt = (Patient) next.getResource();
String nextStr = nextPt.getName().size() > 0 ? nextPt.getName().get(0).getGivenAsSingleString() + " " + nextPt.getName().get(0).getFamily() : "";
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
@AfterClass
public static void afterClassClearContextBaseResourceProviderR4Test() throws Exception {
ourServer.stop();
ourHttpClient.close();
ourServer = null;
ourHttpClient = null;
myValidationSupport.flush();
myValidationSupport = null;
ourWebApplicationContext.close();
ourWebApplicationContext = null;
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.util.TestUtil;
public class CorsR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CorsR4Test.class);
@Test
public void saveLocalOrigin() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/Patient?name=test");
get.addHeader("Origin", "file://");
CloseableHttpResponse resp = ourHttpClient.execute(get);
ourLog.info(resp.toString());
IOUtils.closeQuietly(resp.getEntity().getContent());
assertEquals(200, resp.getStatusLine().getStatusCode());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,208 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.junit.*;
import com.google.common.base.Charsets;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.TestUtil;
public class PatientEverythingR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientEverythingR4Test.class);
private String orgId;
private String patId;
private String encId1;
private String encId2;
private ArrayList<String> myObsIds;
private String myWrongPatId;
private String myWrongEnc1;
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setEverythingIncludesFetchPageSize(new DaoConfig().getEverythingIncludesFetchPageSize());
}
@Override
public void before() throws Exception {
super.before();
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myDaoConfig.setAllowMultipleDelete(true);
Organization org = new Organization();
org.setName("an org");
orgId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless().getValue();
ourLog.info("OrgId: {}", orgId);
Patient patient = new Patient();
patient.getManagingOrganization().setReference(orgId);
patId = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless().getValue();
Patient patient2 = new Patient();
patient2.getManagingOrganization().setReference(orgId);
myWrongPatId = ourClient.create().resource(patient2).execute().getId().toUnqualifiedVersionless().getValue();
Encounter enc1 = new Encounter();
enc1.setStatus(EncounterStatus.CANCELLED);
enc1.getSubject().setReference(patId);
enc1.getServiceProvider().setReference(orgId);
encId1 = ourClient.create().resource(enc1).execute().getId().toUnqualifiedVersionless().getValue();
Encounter enc2 = new Encounter();
enc2.setStatus(EncounterStatus.ARRIVED);
enc2.getSubject().setReference(patId);
enc2.getServiceProvider().setReference(orgId);
encId2 = ourClient.create().resource(enc2).execute().getId().toUnqualifiedVersionless().getValue();
Encounter wrongEnc1 = new Encounter();
wrongEnc1.setStatus(EncounterStatus.ARRIVED);
wrongEnc1.getSubject().setReference(myWrongPatId);
wrongEnc1.getServiceProvider().setReference(orgId);
myWrongEnc1 = ourClient.create().resource(wrongEnc1).execute().getId().toUnqualifiedVersionless().getValue();
myObsIds = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
Observation obs = new Observation();
obs.getSubject().setReference(patId);
obs.setStatus(ObservationStatus.FINAL);
String obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless().getValue();
myObsIds.add(obsId);
}
}
/**
* See #674
*/
@Test
public void testEverythingReturnsCorrectResources() throws Exception {
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=100", EncodingEnum.JSON);
assertNull(bundle.getLink("next"));
Set<String> actual = new TreeSet<String>();
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
actual.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
ourLog.info("Found IDs: {}", actual);
assertThat(actual, hasItem(patId));
assertThat(actual, hasItem(encId1));
assertThat(actual, hasItem(encId2));
assertThat(actual, hasItem(orgId));
assertThat(actual, hasItems(myObsIds.toArray(new String[0])));
assertThat(actual, not(hasItem(myWrongPatId)));
assertThat(actual, not(hasItem(myWrongEnc1)));
}
/**
* See #674
*/
@Test
public void testEverythingReturnsCorrectResourcesSmallPage() throws Exception {
myDaoConfig.setEverythingIncludesFetchPageSize(1);
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=100", EncodingEnum.JSON);
assertNull(bundle.getLink("next"));
Set<String> actual = new TreeSet<String>();
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
actual.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
ourLog.info("Found IDs: {}", actual);
assertThat(actual, hasItem(patId));
assertThat(actual, hasItem(encId1));
assertThat(actual, hasItem(encId2));
assertThat(actual, hasItem(orgId));
assertThat(actual, hasItems(myObsIds.toArray(new String[0])));
assertThat(actual, not(hasItem(myWrongPatId)));
assertThat(actual, not(hasItem(myWrongEnc1)));
}
/**
* See #674
*/
@Test
public void testEverythingPagesWithCorrectEncodingJson() throws Exception {
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=1", EncodingEnum.JSON);
assertNotNull(bundle.getLink("next").getUrl());
assertThat(bundle.getLink("next").getUrl(), containsString("_format=json"));
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.JSON);
assertNotNull(bundle.getLink("next").getUrl());
assertThat(bundle.getLink("next").getUrl(), containsString("_format=json"));
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.JSON);
}
/**
* See #674
*/
@Test
public void testEverythingPagesWithCorrectEncodingXml() throws Exception {
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=xml&_count=1", EncodingEnum.XML);
assertNotNull(bundle.getLink("next").getUrl());
ourLog.info("Next link: {}", bundle.getLink("next").getUrl());
assertThat(bundle.getLink("next").getUrl(), containsString("_format=xml"));
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.XML);
assertNotNull(bundle.getLink("next").getUrl());
ourLog.info("Next link: {}", bundle.getLink("next").getUrl());
assertThat(bundle.getLink("next").getUrl(), containsString("_format=xml"));
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.XML);
}
private Bundle fetchBundle(String theUrl, EncodingEnum theEncoding) throws IOException, ClientProtocolException {
Bundle bundle;
HttpGet get = new HttpGet(theUrl);
CloseableHttpResponse resp = ourHttpClient.execute(get);
try {
assertEquals(theEncoding.getResourceContentTypeNonLegacy(), resp.getFirstHeader(ca.uhn.fhir.rest.api.Constants.HEADER_CONTENT_TYPE).getValue().replaceAll(";.*", ""));
bundle = theEncoding.newParser(myFhirCtx).parseResource(Bundle.class, IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8));
} finally {
IOUtils.closeQuietly(resp);
}
return bundle;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,13 @@
package ca.uhn.fhir.jpa.provider.r4;
import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.instance.model.api.IBaseResource;
public class QuestionnaireResourceProviderR4 extends JpaResourceProviderR4<Questionnaire> {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Questionnaire.class;
}
}

View File

@ -0,0 +1,428 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CapabilityStatement.*;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.SearchParameter.XPathUsageType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.gclient.ReferenceClientParam;
import ca.uhn.fhir.rest.gclient.TokenClientParam;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProviderR4Test {
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
}
@Override
public void before() throws Exception {
super.before();
}
@Test
public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException {
SearchParameter sp = new SearchParameter();
sp.setCode("foo");
sp.setExpression("Patient.gender");
sp.setXpathUsage(XPathUsageType.NORMAL);
sp.setTitle("Foo Param");
try {
ourClient.create().resource(sp).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@Test
public void testIncludeExtensionReferenceAsRecurse() throws Exception, IOException {
SearchParameter attendingSp = new SearchParameter();
attendingSp.addBase("Patient");
attendingSp.setCode("attending");
attendingSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
attendingSp.setTitle("Attending");
attendingSp.setExpression("Patient.extension('http://acme.org/attending')");
attendingSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
attendingSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
attendingSp.getTarget().add(new CodeType("Practitioner"));
IIdType spId = mySearchParameterDao.create(attendingSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
Practitioner p1 = new Practitioner();
p1.addName().setFamily("P1");
IIdType p1id = myPractitionerDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addName().setFamily("P2");
p2.addExtension().setUrl("http://acme.org/attending").setValue(new Reference(p1id));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Appointment app = new Appointment();
app.addParticipant().getActor().setReference(p2id.getValue());
IIdType appId = myAppointmentDao.create(app).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
HttpGet get = new HttpGet(ourServerBase + "/Appointment?_include:recurse=Appointment:patient&_include:recurse=Appointment:location&_include:recurse=Patient:attending&_pretty=true");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
String resp = IOUtils.toString(response.getEntity().getContent(), Constants.CHARSET_UTF8);
ourLog.info(resp);
assertEquals(200, response.getStatusLine().getStatusCode());
assertThat(resp, containsString("<fullUrl value=\"http://localhost:" + ourPort + "/fhir/context/Practitioner/"));
} finally {
IOUtils.closeQuietly(response);
}
}
@Test
public void testSearchForExtension() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
ourClient
.create()
.resource(eyeColourSp)
.execute();
// mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p1));
Patient p2 = new Patient();
p2.setActive(true);
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Bundle bundle = ourClient
.search()
.forResource(Patient.class)
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
.returnBundle(Bundle.class)
.execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
List<String> foundResources = toUnqualifiedVersionlessIdValues(bundle);
assertThat(foundResources, contains(p1id.getValue()));
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamR4Test.class);
@Override
@Before
public void beforeResetConfig() {
super.beforeResetConfig();
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
mySearchParamRegsitry.forceRefresh();
}
@Test
public void testConformanceOverrideAllowed() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
CapabilityStatement conformance = ourClient
.fetchConformance()
.ofType(CapabilityStatement.class)
.execute();
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
assertNull(param);
param = map.get("gender");
assertNotNull(param);
TransactionTemplate txTemplate = newTxTemplate();
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
}
});
// Disable an existing parameter
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
}
});
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
mySearchParamRegsitry.forceRefresh();
}
});
conformance = ourClient
.fetchConformance()
.ofType(CapabilityStatement.class)
.execute();
map = extractSearchParams(conformance, "Patient");
param = map.get("foo");
assertEquals("foo", param.getName());
param = map.get("gender");
assertNull(param);
}
@Test
public void testConformanceOverrideNotAllowed() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
CapabilityStatement conformance = ourClient
.fetchConformance()
.ofType(CapabilityStatement.class)
.execute();
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = extractSearchParams(conformance, "Patient");
CapabilityStatementRestResourceSearchParamComponent param = map.get("foo");
assertNull(param);
param = map.get("gender");
assertNotNull(param);
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
// Disable an existing parameter
fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
mySearchParamRegsitry.forceRefresh();
conformance = ourClient
.fetchConformance()
.ofType(CapabilityStatement.class)
.execute();
map = extractSearchParams(conformance, "Patient");
param = map.get("foo");
assertEquals("foo", param.getName());
param = map.get("gender");
assertNotNull(param);
}
private Map<String, CapabilityStatementRestResourceSearchParamComponent> extractSearchParams(CapabilityStatement conformance, String resType) {
Map<String, CapabilityStatementRestResourceSearchParamComponent> map = new HashMap<String, CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent>();
for (CapabilityStatementRestComponent nextRest : conformance.getRest()) {
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
if (!resType.equals(nextResource.getType())) {
continue;
}
for (CapabilityStatementRestResourceSearchParamComponent nextParam : nextResource.getSearchParam()) {
map.put(nextParam.getName(), nextParam);
}
}
}
return map;
}
@SuppressWarnings("unused")
@Test
public void testSearchWithCustomParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient pat = new Patient();
pat.setGender(AdministrativeGender.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Patient pat2 = new Patient();
pat2.setGender(AdministrativeGender.FEMALE);
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
Bundle result;
result = ourClient
.search()
.forResource(Patient.class)
.where(new TokenClientParam("foo").exactly().code("male"))
.returnBundle(Bundle.class)
.execute();
foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(patId.getValue()));
}
@Test
public void testCreatingParamMarksCorrectResourcesForReindexing() {
Patient pat = new Patient();
pat.setGender(AdministrativeGender.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.setStatus(ObservationStatus.FINAL);
IIdType obsId = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless();
ResourceTable res = myResourceTableDao.findOne(patId.getIdPartAsLong());
assertEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXED, res.getIndexStatus().longValue());
res = myResourceTableDao.findOne(obsId.getIdPartAsLong());
assertEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXED, res.getIndexStatus().longValue());
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
res = myResourceTableDao.findOne(patId.getIdPartAsLong());
assertEquals(null, res.getIndexStatus());
res = myResourceTableDao.findOne(obsId.getIdPartAsLong());
assertEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXED, res.getIndexStatus().longValue());
}
@SuppressWarnings("unused")
@Test
public void testSearchQualifiedWithCustomReferenceParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.REFERENCE);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Observation.subject");
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient pat = new Patient();
pat.setGender(AdministrativeGender.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Observation obs1 = new Observation();
obs1.getSubject().setReferenceElement(patId);
IIdType obsId1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless();
Observation obs2 = new Observation();
obs2.setStatus(org.hl7.fhir.r4.model.Observation.ObservationStatus.FINAL);
IIdType obsId2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
Bundle result;
result = ourClient
.search()
.forResource(Observation.class)
.where(new ReferenceClientParam("foo").hasChainedProperty(Patient.GENDER.exactly().code("male")))
.returnBundle(Bundle.class)
.execute();
foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(obsId1.getValue()));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,239 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderInterceptorR4Test.class);
private IServerInterceptor myDaoInterceptor;
private IServerInterceptor myServerInterceptor;
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.getInterceptors().remove(myDaoInterceptor);
ourRestServer.unregisterInterceptor(myServerInterceptor);
}
@Override
public void before() throws Exception {
super.before();
myServerInterceptor = mock(IServerInterceptor.class);
myDaoInterceptor = mock(IServerInterceptor.class);
resetServerInterceptor();
myDaoConfig.getInterceptors().add(myDaoInterceptor);
ourRestServer.registerInterceptor(myServerInterceptor);
ourRestServer.registerInterceptor(new InterceptorAdapter() {
@Override
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) {
super.incomingRequestPreHandled(theOperation, theProcessedRequest);
}
});
}
private void resetServerInterceptor() throws ServletException, IOException {
reset(myServerInterceptor);
when(myServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
when(myServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
}
@Test
public void testCreateResource() throws IOException {
String methodName = "testCreateResource";
Patient pt = new Patient();
pt.addName().setFamily(methodName);
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response was: {}", resp);
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
} finally {
response.close();
}
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue());
assertEquals("Patient", ardCaptor.getValue().getResourceType());
assertNotNull(ardCaptor.getValue().getResource());
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
verify(myDaoInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue());
assertEquals("Patient", ardCaptor.getValue().getResourceType());
assertNotNull(ardCaptor.getValue().getResource());
}
@Test
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
String methodName = "testCreateResourceWithVersionedReference";
Organization org = new Organization();
org.setName("orgName");
IIdType orgId = ourClient.create().resource(org).execute().getId().toUnqualified();
assertNotNull(orgId.getVersionIdPartAsLong());
resetServerInterceptor();
Patient pt = new Patient();
pt.addName().setFamily(methodName);
pt.setManagingOrganization(new Reference(orgId));
IParser parser = myFhirCtx.newXmlParser();
parser.setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
parser.setPrettyPrint(true);
String resource = parser.encodeResourceToString(pt);
ourLog.info(resource);
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response was: {}", resp);
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
} finally {
response.close();
}
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue());
assertEquals("Patient", ardCaptor.getValue().getResourceType());
assertNotNull(ardCaptor.getValue().getResource());
Patient patient;
patient = (Patient) ardCaptor.getAllValues().get(0).getResource();
assertEquals(orgId.getValue(), patient.getManagingOrganization().getReference());
}
@Test
public void testCreateResourceInTransaction() throws IOException {
String methodName = "testCreateResourceInTransaction";
Patient pt = new Patient();
pt.addName().setFamily(methodName);
Bundle bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
BundleEntryComponent entry = bundle.addEntry();
entry.setFullUrl("Patient");
entry.setResource(pt);
entry.getRequest().setMethod(HTTPVerb.POST);
entry.getRequest().setUrl("Patient");
String resource = myFhirCtx.newXmlParser().encodeResourceToString(bundle);
HttpPost post = new HttpPost(ourServerBase + "/");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
} finally {
response.close();
}
/*
* Server Interceptor
*/
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
verify(myServerInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
assertEquals(null, ardCaptor.getAllValues().get(0).getResourceType());
assertNotNull(ardCaptor.getAllValues().get(0).getResource());
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType());
assertNotNull(ardCaptor.getAllValues().get(1).getResource());
ArgumentCaptor<RequestDetails> rdCaptor = ArgumentCaptor.forClass(RequestDetails.class);
ArgumentCaptor<HttpServletRequest> srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
ArgumentCaptor<HttpServletResponse> sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class);
verify(myServerInterceptor, times(1)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture());
/*
* DAO Interceptor
*/
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
verify(myDaoInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
assertEquals(RestOperationTypeEnum.TRANSACTION, opTypeCaptor.getAllValues().get(0));
assertEquals("Bundle", ardCaptor.getAllValues().get(0).getResourceType());
assertNotNull(ardCaptor.getAllValues().get(0).getResource());
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType());
assertNotNull(ardCaptor.getAllValues().get(1).getResource());
rdCaptor = ArgumentCaptor.forClass(RequestDetails.class);
srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class);
verify(myDaoInterceptor, times(0)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,235 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
public class ResourceProviderQuestionnaireResponseR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderQuestionnaireResponseR4Test.class);
private static RequestValidatingInterceptor ourValidatingInterceptor;
@AfterClass
public static void afterClassClearContext() {
ourRestServer.unregisterInterceptor(ourValidatingInterceptor);
ourValidatingInterceptor = null;
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Override
@Before
public void before() throws Exception {
super.before();
if (ourValidatingInterceptor == null) {
ourValidatingInterceptor = new RequestValidatingInterceptor();
ourValidatingInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
Collection<IValidatorModule> validators = myAppCtx.getBeansOfType(IValidatorModule.class).values();
for (IValidatorModule next : validators) {
ourValidatingInterceptor.addValidatorModule(next);
}
ourRestServer.registerInterceptor(ourValidatingInterceptor);
}
}
@SuppressWarnings("unused")
@Test
public void testCreateWithLocalReference() {
Patient pt1 = new Patient();
pt1.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Questionnaire q1 = new Questionnaire();
q1.addItem().setLinkId("link1").setType(QuestionnaireItemType.STRING);
IIdType qId = myQuestionnaireDao.create(q1, mySrd).getId().toUnqualifiedVersionless();
QuestionnaireResponse qr1 = new QuestionnaireResponse();
qr1.getQuestionnaire().setReferenceElement(qId);
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
try {
ourClient.create().resource(qr1).execute();
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.toString(), containsString("Answer value must be of type string"));
}
}
@SuppressWarnings("unused")
@Test
public void testCreateWithAbsoluteReference() {
Patient pt1 = new Patient();
pt1.addName().setFamily("Everything").addGiven("Arthur");
IIdType ptId1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Questionnaire q1 = new Questionnaire();
q1.addItem().setLinkId("link1").setType(QuestionnaireItemType.STRING);
IIdType qId = myQuestionnaireDao.create(q1, mySrd).getId().toUnqualifiedVersionless();
QuestionnaireResponse qr1 = new QuestionnaireResponse();
qr1.getQuestionnaire().setReferenceElement(qId.withServerBase("http://example.com", "Questionnaire"));
qr1.setStatus(QuestionnaireResponseStatus.COMPLETED);
qr1.addItem().setLinkId("link1").addAnswer().setValue(new DecimalType(123));
try {
ourClient.create().resource(qr1).execute();
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.toString(), containsString("Answer value must be of type string"));
}
}
@Test
public void testSaveQuestionnaire() throws Exception {
String input = "<QuestionnaireResponse xmlns=\"http://hl7.org/fhir\">\n" +
" <status value=\"completed\"/>\n" +
" <authored value=\"2016-05-03T13:05:20-04:00\"/>\n" +
" <item>\n" +
" <linkId value=\"breast-feeding-intention\"/>\n" +
" <text value=\"Breast Feeding Intention:\"/>\n" +
" <answer>\n" +
" <valueCoding>\n" +
" <system value=\"http://example.org/codesystem-breastfeeding-intention\"/>\n" +
" <code value=\"true\"/>\n" +
" <display value=\"Mother wants to provide formula exclusively\"/>\n" +
" </valueCoding>\n" +
" </answer>\n" +
" </item>\n" +
" <item>\n" +
" <linkId value=\"breast-feeding-education\"/>\n" +
" <text value=\"Answer if not exclusive BM:\"/>\n" +
" <answer>\n" +
" <valueCoding>\n" +
" <system value=\"http://example.org/codesystem-breastfeeding-education\"/>\n" +
" <code value=\"true\"/>\n" +
" <display value=\"Mother not given comprehensive education per protocol\"/>\n" +
" </valueCoding>\n" +
" </answer>\n" +
" </item>\n" +
" <item>\n" +
" <linkId value=\"breast-feeding-exclusion\"/>\n" +
" <text value=\"Exclusion Criteria:\"/>\n" +
" <answer>\n" +
" <valueCoding>\n" +
" <system value=\"http://example.org/codesystem-breastfeeding-exclusion\"/>\n" +
" <code value=\"true\"/>\n" +
" <display\n" +
" value=\"Maternal use of drugs of abuse, antimetabolites, chemotherapeutic agents, or radioisotopes\"\n" +
" />\n" +
" </valueCoding>\n" +
" </answer>\n" +
" </item>\n" +
"</QuestionnaireResponse>";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse");
post.setEntity(new StringEntity(input, ContentType.create(ca.uhn.fhir.rest.api.Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
final IdType id2;
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(ca.uhn.fhir.rest.api.Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/QuestionnaireResponse/"));
id2 = new IdType(newIdString);
} finally {
IOUtils.closeQuietly(response);
}
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/" + id2.getIdPart() + "?_format=xml&_pretty=true");
response = ourHttpClient.execute(get);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("Exclusion Criteria"));
} finally {
IOUtils.closeQuietly(response);
}
}
@Test
public void testValidateOnNoId() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("No resource supplied for $validate operation"));
assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertEquals(200, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("Resource has no ID"));
assertEquals(422, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.junit.Assert.*;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Composition;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderR4BundleTest extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4BundleTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
/**
* See #401
*/
@Test
public void testBundlePreservesFullUrl() throws Exception {
Bundle bundle = new Bundle();
bundle.setType(BundleType.DOCUMENT);
Composition composition = new Composition();
composition.setTitle("Visit Summary");
bundle.addEntry().setFullUrl("http://foo").setResource(composition);
IIdType id = ourClient.create().resource(bundle).execute().getId();
Bundle retBundle = ourClient.read().resource(Bundle.class).withId(id).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(retBundle));
assertEquals("http://foo", bundle.getEntry().get(0).getFullUrl());
}
}

View File

@ -0,0 +1,264 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemTest.class);
private IIdType myExtensionalVsId;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@Transactional
public void before02() throws IOException {
CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
myCodeSystemDao.create(cs, mySrd);
ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testLookupOnExternalCode() {
ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd);
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue());
// With HTTP GET
respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ParentA"))
.andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM))
.useHttpGet()
.execute();
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue());
}
@Test
public void testLookupOperationByCodeAndSystemBuiltInCode() {
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ACSN"))
.andParameter("system", new UriType("http://hl7.org/fhir/v2/0203"))
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Accession ID", ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue());
}
@Test
public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() {
//@formatter:off
try {
ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("ACSNAAAAAA"))
.andParameter("system", new UriType("http://hl7.org/fhir/v2/0203"))
.execute();
fail();
} catch (ResourceNotFoundException e) {
// good
}
//@formatter:on
}
@Test
public void testLookupOperationByCodeAndSystemUserDefinedCode() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue());
}
@Test
public void testLookupOperationByCodeAndSystemUserDefinedNonExistantCode() {
//@formatter:off
try {
ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("8450-9AAAAA"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
fail();
} catch (ResourceNotFoundException e) {
// good
}
//@formatter:on
}
@Test
public void testLookupOperationByCoding() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue());
}
@Test
public void testLookupOperationByInvalidCombination() {
//@formatter:off
try {
ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9"))
.andParameter("code", new CodeType("8450-9"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage());
}
//@formatter:on
}
@Test
public void testLookupOperationByInvalidCombination2() {
//@formatter:off
try {
ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: $lookup can only validate (system AND code) OR (coding.system AND coding.code)", e.getMessage());
}
//@formatter:on
}
@Test
public void testLookupOperationByInvalidCombination3() {
//@formatter:off
try {
ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode(null))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate", e.getMessage());
}
//@formatter:on
}
@Test
// @Ignore
public void testLookupOperationForBuiltInCode() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "code", new CodeType("M"))
.andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Married", ((StringType)respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue());
}
}

View File

@ -0,0 +1,533 @@
package ca.uhn.fhir.jpa.provider.r4;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.FilterOperator;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4ValueSetTest.class);
private IIdType myExtensionalVsId;
private IIdType myLocalValueSetId;
private ValueSet myLocalVs;
@Before
@Transactional
public void before02() throws IOException {
CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
myCodeSystemDao.create(cs, mySrd);
ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
}
/**
* #516
*/
@Test
public void testInvalidFilter() throws Exception {
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
HttpPost post = new HttpPost(ourServerBase+"/ValueSet/%24expand");
post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW)));
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
ourLog.info(resp.toString());
assertEquals(400, resp.getStatusLine().getStatusCode());
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
}finally {
IOUtils.closeQuietly(resp);
}
}
private CodeSystem createExternalCs() {
IFhirResourceDao<CodeSystem> codeSystemDao = myCodeSystemDao;
IResourceTableDao resourceTableDao = myResourceTableDao;
IHapiTerminologySvc termSvc = myTermSvc;
return createExternalCs(codeSystemDao, resourceTableDao, termSvc, mySrd);
}
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, IHapiTerminologySvc theTermSvc, ServletRequestDetails theRequestDetails) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
createLocalVs(codeSystem);
}
private void createExternalCsAndLocalVsWithUnknownCode() {
CodeSystem codeSystem = createExternalCs();
createLocalVsWithUnknownCode(codeSystem);
}
private void createLocalCsAndVs() {
//@formatter:off
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
codeSystem
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA")
.addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA"))
)
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB"));
codeSystem
.addConcept().setCode("B").setDisplay("Code B")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB"));
//@formatter:on
myCodeSystemDao.create(codeSystem, mySrd);
createLocalVs(codeSystem);
}
private void createLocalVs(CodeSystem codeSystem) {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem(codeSystem.getUrl());
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childAA");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
private void createLocalVsPointingAtBuiltInCodeSystem() {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem("http://hl7.org/fhir/v3/MaritalStatus");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
private void createLocalVsWithUnknownCode(CodeSystem codeSystem) {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem(codeSystem.getUrl());
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testExpandById() throws IOException {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
assertThat(resp, containsString("<expansion>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"8450-9\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure--expiration\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("<contains>"));
assertThat(resp, containsString("<system value=\"http://acme.org\"/>"));
assertThat(resp, containsString("<code value=\"11378-7\"/>"));
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, containsString("</contains>"));
assertThat(resp, containsString("</expansion>"));
}
@Test
public void testExpandByIdentifier() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
//
@Test
public void testExpandByIdWithFilter() throws IOException {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "filter", new StringType("first"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>")));
}
@Test
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
@Test
public void testExpandInlineVsAgainstBuiltInCs() throws IOException {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", myLocalVs)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandInlineVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalVs);
myLocalVs.setId("");
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", myLocalVs)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test
public void testExpandInvalidParams() throws IOException {
//@formatter:off
try {
ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: $expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request", e.getMessage());
}
//@formatter:on
//@formatter:off
try {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-r4.xml");
ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
}
//@formatter:on
//@formatter:off
try {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-r4.xml");
ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
}
//@formatter:on
}
@Test
public void testExpandLocalVsAgainstBuiltInCs() throws IOException {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandLocalVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test
public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType(URL_MY_VALUE_SET))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test
public void testExpandLocalVsWithUnknownCode() throws IOException {
createExternalCsAndLocalVsWithUnknownCode();
assertNotNull(myLocalValueSetId);
//@formatter:off
try {
ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Invalid filter criteria - code does not exist: {http://example.com/my_code_system}childFOOOOOOO", e.getMessage());
}
//@formatter:on
}
@Test
public void testValiedateCodeAgainstBuiltInSystem() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("identifier", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType)respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType)respParam.getParameter().get(2).getValue()).getValue());
}
@Test
public void testValidateCodeOperationByCodeAndSystemInstance() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myExtensionalVsId)
.named("validate-code")
.withParameter(Parameters.class, "code", new CodeType("8495-4"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
}
@Test
public void testValidateCodeOperationByCodeAndSystemType() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,68 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.util.TestUtil;
public class ServerR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerR4Test.class);
/**
* See #519
*/
@Test
public void saveIdParamOnlyAppearsOnce() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/metadata?_pretty=true&_format=xml");
CloseableHttpResponse resp = ourHttpClient.execute(get);
try {
ourLog.info(resp.toString());
assertEquals(200, resp.getStatusLine().getStatusCode());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
CapabilityStatement cs = myFhirCtx.newXmlParser().parseResource(CapabilityStatement.class, respString);
for (CapabilityStatementRestResourceComponent nextResource : cs.getRest().get(0).getResource()) {
ourLog.info("Testing resource: " + nextResource.getType());
Set<String> sps = new HashSet<String>();
for (CapabilityStatementRestResourceSearchParamComponent nextSp : nextResource.getSearchParam()) {
if (sps.add(nextSp.getName()) == false) {
fail("Duplicate search parameter " + nextSp.getName() + " for resource " + nextResource.getType());
}
}
if (!sps.contains("_id")) {
fail("No search parameter _id for resource " + nextResource.getType());
}
}
} finally {
IOUtils.closeQuietly(resp.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,97 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.blankOrNullString;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r4.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.util.AopTestUtils;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.util.TestUtil;
public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcR4Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@After()
public void after() throws Exception {
super.after();
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
staleSearchDeletingSvc.setCutoffSlackForUnitTest(StaleSearchDeletingSvcImpl.DEFAULT_CUTOFF_SLACK);
}
@Before
public void before() throws Exception {
super.before();
StaleSearchDeletingSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(myStaleSearchDeletingSvc);
staleSearchDeletingSvc.setCutoffSlackForUnitTest(0);
}
@Test
public void testEverythingInstanceWithContentFilter() throws Exception {
for (int i = 0; i < 20; i++) {
Patient pt1 = new Patient();
pt1.addName().setFamily("Everything").addGiven("Arthur");
myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
}
//@formatter:off
IClientExecutable<IQuery<Bundle>, Bundle> search = ourClient
.search()
.forResource(Patient.class)
.where(Patient.NAME.matches().value("Everything"))
.returnBundle(Bundle.class);
//@formatter:on
Bundle resp1 = search.execute();
for (int i = 0; i < 20; i++) {
search.execute();
}
BundleLinkComponent nextLink = resp1.getLink("next");
assertNotNull(nextLink);
String nextLinkUrl = nextLink.getUrl();
assertThat(nextLinkUrl, not(blankOrNullString()));
Bundle resp2 = ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp2));
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
Thread.sleep(20);
myDaoConfig.setExpireSearchResultsAfterMillis(10);
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
try {
ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
fail();
} catch (ResourceGoneException e) {
assertThat(e.getMessage(), containsString("does not exist and may have expired"));
}
}
}

View File

@ -0,0 +1,515 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.junit.Assert.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.*;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorR4;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class SubscriptionsR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsR4Test.class);
private static final String WEBSOCKET_PATH = "/websocket/r4";
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@Override
public void beforeCreateInterceptor() {
super.beforeCreateInterceptor();
SubscriptionsRequireManualActivationInterceptorR4 interceptor = new SubscriptionsRequireManualActivationInterceptorR4();
interceptor.setDao(mySubscriptionDao);
myDaoConfig.getInterceptors().add(interceptor);
}
@Before
public void beforeEnableScheduling() {
myDaoConfig.setSchedulingDisabled(false);
}
private void sleepUntilPingCount(BaseSocket socket, int wantPingCount) throws InterruptedException {
/*
* In a separate thread, start a polling for new resources. Normally the scheduler would
* take care of this, but that can take longer which makes the unit tests run much slower
* so we simulate that part..
*/
new Thread() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
ourLog.warn("Interrupted", e);
}
ourLog.info("About to poll in separate thread");
mySubscriptionDao.pollForNewUndeliveredResources();
ourLog.info("Done poll in separate thread");
}}.start();
ourLog.info("Entering loop");
for (long start = System.currentTimeMillis(), now = System.currentTimeMillis(); now - start <= 20000; now = System.currentTimeMillis()) {
ourLog.debug("Starting");
if (socket.myError != null) {
fail(socket.myError);
}
if (socket.myPingCount >= wantPingCount) {
ourLog.info("Breaking loop");
break;
}
ourLog.debug("Sleeping");
Thread.sleep(100);
}
ourLog.info("Out of loop, pingcount {} error {}", socket.myPingCount, socket.myError);
assertNull(socket.myError, socket.myError);
assertEquals(wantPingCount, socket.myPingCount);
}
private void stopClientAndWaitForStopped(WebSocketClient client) throws Exception {
client.stop();
/*
* When websocket closes, the subscription is automatically deleted. This
* can cause deadlocks if the test returns too quickly since it also
* tries to delete the subscription
*/
Bundle found = null;
for (int i = 0; i < 10; i++) {
found = ourClient.search().forResource("Subscription").returnBundle(Bundle.class).execute();
if (found.getEntry().size() > 0) {
Thread.sleep(200);
} else {
break;
}
}
assertEquals(0, found.getEntry().size());
}
@Test
public void testCreateInvalidNoStatus() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("application/fhir+json");
subs.getChannel().setEndpoint("http://localhost:8888");
subs.setCriteria("Observation?identifier=123");
try {
ourClient.create().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Can not create resource: Subscription.status must be populated", e.getMessage());
}
subs.setId("ABC");
try {
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Can not create resource: Subscription.status must be populated", e.getMessage());
}
subs.setStatus(SubscriptionStatus.REQUESTED);
ourClient.update().resource(subs).execute();
}
@Test
public void testCreateInvalidWrongStatus() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.setStatus(SubscriptionStatus.ACTIVE);
subs.setCriteria("Observation?identifier=123");
try {
ourClient.create().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'off' or 'requested' on a newly created subscription", e.getMessage());
}
subs.setId("ABC");
try {
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'off' or 'requested' on a newly created subscription", e.getMessage());
}
}
@Test
public void testSubscriptionDynamic() throws Exception {
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionDynamic";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
String criteria = "Observation?subject=Patient/" + pId.getIdPart();
DynamicEchoSocket socket = new DynamicEchoSocket(criteria, EncodingEnum.JSON);
WebSocketClient client = new WebSocketClient();
try {
client.start();
URI echoUri = new URI("ws://localhost:" + ourPort + WEBSOCKET_PATH);
client.connect(socket, echoUri, new ClientUpgradeRequest());
ourLog.info("Connecting to : {}", echoUri);
sleepUntilPingCount(socket, 1);
mySubscriptionDao.read(new IdDt("Subscription", socket.mySubsId), mySrd);
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
sleepUntilPingCount(socket, 3);
obs = (Observation) socket.myReceived.get(0);
assertEquals(afterId1.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
obs = (Observation) socket.myReceived.get(1);
assertEquals(afterId2.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
sleepUntilPingCount(socket, 4);
obs = (Observation) socket.myReceived.get(2);
assertEquals(afterId3.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
} finally {
try {
stopClientAndWaitForStopped(client);
} catch (Exception e) {
ourLog.error("Failure", e);
fail(e.getMessage());
}
}
}
@Test
public void testSubscriptionDynamicXml() throws Exception {
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionDynamic";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
String criteria = "Observation?subject=Patient/" + pId.getIdPart() + "&_format=xml";
DynamicEchoSocket socket = new DynamicEchoSocket(criteria, EncodingEnum.XML);
WebSocketClient client = new WebSocketClient();
try {
client.start();
URI echoUri = new URI("ws://localhost:" + ourPort + WEBSOCKET_PATH);
client.connect(socket, echoUri, new ClientUpgradeRequest());
ourLog.info("Connecting to : {}", echoUri);
sleepUntilPingCount(socket, 1);
mySubscriptionDao.read(new IdDt("Subscription", socket.mySubsId), mySrd);
Observation obs = new Observation();
obs.getMeta().addProfile("http://foo");
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
sleepUntilPingCount(socket, 3);
obs = (Observation) socket.myReceived.get(0);
assertEquals(afterId1.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
obs = (Observation) socket.myReceived.get(1);
assertEquals(afterId2.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
sleepUntilPingCount(socket, 4);
obs = (Observation) socket.myReceived.get(2);
assertEquals(afterId3.getValue(), obs.getIdElement().toUnqualifiedVersionless().getValue());
} finally {
try {
stopClientAndWaitForStopped(client);
} catch (Exception e) {
ourLog.error("Failure", e);
fail(e.getMessage());
}
}
}
@Test
public void testSubscriptionSimple() throws Exception {
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionResourcesAppear";
Patient p = new Patient();
p.addName().setFamily(methodName);
IIdType pId = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
Subscription subs = new Subscription();
subs.getMeta().addProfile("http://foo");
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatus.ACTIVE);
String subsId = mySubscriptionDao.create(subs, mySrd).getId().getIdPart();
Thread.sleep(100);
Observation obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
Thread.sleep(100);
WebSocketClient client = new WebSocketClient();
SimpleEchoSocket socket = new SimpleEchoSocket(subsId);
try {
client.start();
URI echoUri = new URI("ws://localhost:" + ourPort + WEBSOCKET_PATH);
ClientUpgradeRequest request = new ClientUpgradeRequest();
client.connect(socket, echoUri, request);
ourLog.info("Connecting to : {}", echoUri);
sleepUntilPingCount(socket, 1);
obs = new Observation();
obs.getSubject().setReferenceElement(pId);
obs.setStatus(ObservationStatus.FINAL);
IIdType afterId3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
sleepUntilPingCount(socket, 2);
} finally {
try {
client.stop();
} catch (Exception e) {
ourLog.error("Failure", e);
fail(e.getMessage());
}
}
}
@Test
public void testUpdateFails() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("application/fhir+json");
subs.getChannel().setEndpoint("http://localhost:8888");
subs.setStatus(SubscriptionStatus.REQUESTED);
subs.setCriteria("Observation?identifier=123");
IIdType id = ourClient.create().resource(subs).execute().getId().toUnqualifiedVersionless();
subs.setId(id);
try {
subs.setStatus(SubscriptionStatus.ACTIVE);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status can not be changed from 'requested' to 'active'", e.getMessage());
}
try {
subs.setStatus((SubscriptionStatus) null);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Can not update resource: Subscription.status must be populated", e.getMessage());
}
subs.setStatus(SubscriptionStatus.OFF);
}
@Test
public void testUpdateToInvalidStatus() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelType.RESTHOOK);
subs.getChannel().setPayload("application/fhir+json");
subs.getChannel().setEndpoint("http://localhost:8888");
subs.setCriteria("Observation?identifier=123");
subs.setStatus(SubscriptionStatus.REQUESTED);
IIdType id = ourClient.create().resource(subs).execute().getId();
subs.setId(id);
try {
subs.setStatus(SubscriptionStatus.ACTIVE);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status can not be changed from 'requested' to 'active'", e.getMessage());
}
try {
subs.setStatus((SubscriptionStatus) null);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Can not update resource: Subscription.status must be populated", e.getMessage());
}
subs.setStatus(SubscriptionStatus.OFF);
ourClient.update().resource(subs).execute();
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public class BaseSocket {
protected String myError;
protected boolean myGotBound;
protected int myPingCount;
protected String mySubsId;
}
/**
* Basic Echo Client Socket
*/
@WebSocket(maxTextMessageSize = 64 * 1024)
public class DynamicEchoSocket extends BaseSocket {
private String myCriteria;
private EncodingEnum myEncoding;
private List<IBaseResource> myReceived = new ArrayList<IBaseResource>();
@SuppressWarnings("unused")
private Session session;
public DynamicEchoSocket(String theCriteria, EncodingEnum theEncoding) {
myCriteria = theCriteria;
myEncoding = theEncoding;
}
@OnWebSocketConnect
public void onConnect(Session session) {
ourLog.info("Got connect: {}", session);
this.session = session;
try {
String sending = "bind " + myCriteria;
ourLog.info("Sending: {}", sending);
session.getRemote().sendString(sending);
} catch (Throwable t) {
ourLog.error("Failure", t);
}
}
@OnWebSocketMessage
public void onMessage(String theMsg) {
ourLog.info("Got msg: {}", theMsg);
if (theMsg.startsWith("bound ")) {
myGotBound = true;
mySubsId = (theMsg.substring("bound ".length()));
myPingCount++;
} else if (myGotBound && theMsg.startsWith("add " + mySubsId + "\n")) {
String text = theMsg.substring(("add " + mySubsId + "\n").length());
IBaseResource res = myEncoding.newParser(myFhirCtx).parseResource(text);
myReceived.add(res);
myPingCount++;
} else {
myError = "Unexpected message: " + theMsg;
}
}
}
/**
* Basic Echo Client Socket
*/
@WebSocket(maxTextMessageSize = 64 * 1024)
public class SimpleEchoSocket extends BaseSocket {
@SuppressWarnings("unused")
private Session session;
public SimpleEchoSocket(String theSubsId) {
mySubsId = theSubsId;
}
@OnWebSocketConnect
public void onConnect(Session session) {
ourLog.info("Got connect: {}", session);
this.session = session;
try {
String sending = "bind " + mySubsId;
ourLog.info("Sending: {}", sending);
session.getRemote().sendString(sending);
} catch (Throwable t) {
ourLog.error("Failure", t);
}
}
@OnWebSocketMessage
public void onMessage(String theMsg) {
ourLog.info("Got msg: {}", theMsg);
if (theMsg.equals("bound " + mySubsId)) {
myGotBound = true;
} else if (myGotBound && theMsg.startsWith("ping " + mySubsId)) {
myPingCount++;
} else {
myError = "Unexpected message: " + theMsg;
}
}
}
}

View File

@ -0,0 +1,662 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
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.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.rp.r4.*;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.ResultSeverityEnum;
public class SystemProviderR4Test extends BaseJpaR4Test {
private static RestfulServer myRestServer;
private static IGenericClient ourClient;
private static FhirContext ourCtx;
private static CloseableHttpClient ourHttpClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderR4Test.class);
private static Server ourServer;
private static String ourServerBase;
private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
@Test
public void testTransactionWithInlineConditionalUrl() throws Exception {
myDaoConfig.setAllowInlineMatchUrlReferences(true);
Patient p = new Patient();
p.addName().setFamily("van de Heuvelcx85ioqWJbI").addGiven("Pietercx85ioqWJbI");
myPatientDao.create(p, mySrd);
Organization o = new Organization();
o.addIdentifier().setSystem("urn:oid:2.16.840.1.113883.2.4.6.1").setValue("07-8975469");
myOrganizationDao.create(o, mySrd);
//@formatter:off
String input = "<Bundle xmlns=\"http://hl7.org/fhir\">\n" +
" <id value=\"20160113160203\"/>\n" +
" <type value=\"transaction\"/>\n" +
" <entry>\n" +
" <fullUrl value=\"urn:uuid:c72aa430-2ddc-456e-7a09-dea8264671d8\"/>\n" +
" <resource>\n" +
" <Encounter>\n" +
" <identifier>\n" +
" <use value=\"official\"/>\n" +
" <system value=\"http://healthcare.example.org/identifiers/encounter\"/>\n" +
" <value value=\"845962.8975469\"/>\n" +
" </identifier>\n" +
" <status value=\"in-progress\"/>\n" +
" <class value=\"inpatient\"/>\n" +
" <patient>\n" +
" <reference value=\"Patient?family=van%20de%20Heuvelcx85ioqWJbI&amp;given=Pietercx85ioqWJbI\"/>\n" +
" </patient>\n" +
" <serviceProvider>\n" +
" <reference value=\"Organization?identifier=urn:oid:2.16.840.1.113883.2.4.6.1|07-8975469\"/>\n" +
" </serviceProvider>\n" +
" </Encounter>\n" +
" </resource>\n" +
" <request>\n" +
" <method value=\"POST\"/>\n" +
" <url value=\"Encounter\"/>\n" +
" </request>\n" +
" </entry>\n" +
"</Bundle>";
//@formatter:off
HttpPost req = new HttpPost(ourServerBase);
req.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
} finally {
IOUtils.closeQuietly(resp.getEntity().getContent());
}
}
@Test
public void testTransactionDeleteWithDuplicateDeletes() throws Exception {
myDaoConfig.setAllowInlineMatchUrlReferences(true);
Patient p = new Patient();
p.addName().setFamily("van de Heuvelcx85ioqWJbI").addGiven("Pietercx85ioqWJbI");
IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
ourClient.read().resource(Patient.class).withId(id);
Bundle inputBundle = new Bundle();
inputBundle.setType(BundleType.TRANSACTION);
inputBundle.addEntry().getRequest().setMethod(HTTPVerb.DELETE).setUrl(id.getValue());
inputBundle.addEntry().getRequest().setMethod(HTTPVerb.DELETE).setUrl(id.getValue());
inputBundle.addEntry().getRequest().setMethod(HTTPVerb.DELETE).setUrl("Patient?name=Pietercx85ioqWJbI");
String input = myFhirCtx.newXmlParser().encodeResourceToString(inputBundle);
HttpPost req = new HttpPost(ourServerBase + "?_pretty=true");
req.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
Bundle response = myFhirCtx.newXmlParser().parseResource(Bundle.class, encoded);
assertEquals(3, response.getEntry().size());
} finally {
IOUtils.closeQuietly(resp.getEntity().getContent());
}
try {
ourClient.read().resource(Patient.class).withId(id).execute();
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Before
public void beforeStartServer() throws Exception {
if (myRestServer == null) {
PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(myPatientDao);
QuestionnaireResourceProviderR4 questionnaireRp = new QuestionnaireResourceProviderR4();
questionnaireRp.setDao(myQuestionnaireDao);
ObservationResourceProvider observationRp = new ObservationResourceProvider();
observationRp.setDao(myObservationDao);
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
organizationRp.setDao(myOrganizationDao);
RestfulServer restServer = new RestfulServer(ourCtx);
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
restServer.setPlainProviders(mySystemProvider);
int myPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(myPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourCtx = FhirContext.forR4();
restServer.setFhirContext(ourCtx);
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourHttpClient = builder.build();
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.setLogRequestAndResponse(true);
myRestServer = restServer;
}
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myRestServer.setPagingProvider(myPagingProvider);
}
@Before
public void before() {
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
ourClient.registerInterceptor(mySimpleHeaderInterceptor);
}
@SuppressWarnings("deprecation")
@After
public void after() {
myRestServer.setUseBrowserFriendlyContentTypes(true);
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
}
@SuppressWarnings("deprecation")
@Test
public void testResponseUsesCorrectContentType() throws Exception {
myRestServer.setUseBrowserFriendlyContentTypes(true);
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
HttpGet get = new HttpGet(ourServerBase);
// get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get);
assertThat(http.getFirstHeader("Content-Type").getValue(), containsString("application/fhir+json"));
}
/**
* FOrmat has changed, source is no longer valid
*/
@Test
@Ignore
public void testValidateUsingIncomingResources() throws Exception {
FhirInstanceValidator val = new FhirInstanceValidator(myValidationSupport);
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
interceptor.addValidatorModule(val);
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
interceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
myRestServer.registerInterceptor(interceptor);
try {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/questionnaire-sdc-profile-example-ussg-fht.xml");
String bundleStr = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
HttpPost req = new HttpPost(ourServerBase);
req.setEntity(new StringEntity(bundleStr, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
//@formatter:off
assertThat(encoded, containsString("Questionnaire/54127-6/_history/"));
//@formatter:on
for (Header next : resp.getHeaders(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_NAME)) {
ourLog.info(next.toString());
}
} finally {
IOUtils.closeQuietly(resp.getEntity().getContent());
}
} finally {
myRestServer.unregisterInterceptor(interceptor);
}
}
@Test
public void testEverythingReturnsCorrectFormatInPagingLink() throws Exception {
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
myRestServer.setPagingProvider(new FifoMemoryPagingProvider(1).setDefaultPageSize(10));
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
myRestServer.registerInterceptor(interceptor);
for (int i = 0; i < 11; i++) {
Patient p = new Patient();
p.addName().setFamily("Name" + i);
ourClient.create().resource(p).execute();
}
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, containsString("_format=json"));
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
http.close();
}
myRestServer.unregisterInterceptor(interceptor);
}
@Test
public void testEverythingReturnsCorrectBundleType() throws Exception {
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
myRestServer.setPagingProvider(new FifoMemoryPagingProvider(1).setDefaultPageSize(10));
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
myRestServer.registerInterceptor(interceptor);
for (int i = 0; i < 11; i++) {
Patient p = new Patient();
p.addName().setFamily("Name" + i);
ourClient.create().resource(p).execute();
}
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode());
Bundle responseBundle = ourCtx.newXmlParser().parseResource(Bundle.class, response);
assertEquals(BundleType.SEARCHSET, responseBundle.getTypeElement().getValue());
} finally {
http.close();
}
myRestServer.unregisterInterceptor(interceptor);
}
@Test
public void testEverythingType() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
http.close();
}
}
@Test
public void testMarkResourcesForReindexing() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(http);;
}
}
@Transactional(propagation = Propagation.NEVER)
@Test
public void testSuggestKeywords() throws Exception {
Patient patient = new Patient();
patient.addName().setFamily("testSuggest");
IIdType ptId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getCode().setText("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL");
obs.getSubject().setReferenceElement(ptId);
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.setId(obsId);
obs.getSubject().setReferenceElement(ptId);
obs.getCode().setText("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL");
myObservationDao.update(obs, mySrd);
HttpGet get = new HttpGet(ourServerBase + "/$suggest-keywords?context=Patient/" + ptId.getIdPart() + "/$everything&searchParam=_content&text=zxc&_pretty=true&_format=xml");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
assertEquals(2, parameters.getParameter().size());
assertEquals("keyword", parameters.getParameter().get(0).getPart().get(0).getName());
assertEquals(("ZXCVBNM"), ((StringType) parameters.getParameter().get(0).getPart().get(0).getValue()).getValueAsString());
assertEquals("score", parameters.getParameter().get(0).getPart().get(1).getName());
assertEquals(("1.0"), ((DecimalType) parameters.getParameter().get(0).getPart().get(1).getValue()).getValueAsString());
} finally {
http.close();
}
}
@Test
public void testSuggestKeywordsInvalid() throws Exception {
Patient patient = new Patient();
patient.addName().setFamily("testSuggest");
IIdType ptId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReferenceElement(ptId);
obs.getCode().setText("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL");
myObservationDao.create(obs, mySrd);
HttpGet get = new HttpGet(ourServerBase + "/$suggest-keywords");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided"));
} finally {
http.close();
}
get = new HttpGet(ourServerBase + "/$suggest-keywords?context=Patient/" + ptId.getIdPart() + "/$everything");
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally {
http.close();
}
get = new HttpGet(ourServerBase + "/$suggest-keywords?context=Patient/" + ptId.getIdPart() + "/$everything&searchParam=aa");
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided"));
} finally {
http.close();
}
}
@Test
public void testGetOperationDefinition() {
OperationDefinition op = ourClient.read(OperationDefinition.class, "-s-get-resource-counts");
assertEquals("get-resource-counts", op.getCode());
}
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@Test
public void testTransactionWithIncompleteBundle() throws Exception {
Patient patient = new Patient();
patient.setGender(AdministrativeGender.MALE);
Bundle bundle = new Bundle();
bundle.setType(BundleType.TRANSACTION);
bundle.addEntry().setResource(patient);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
} catch (InvalidRequestException e) {
assertThat(e.toString(), containsString("missing or invalid HTTP Verb"));
}
}
@Test
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdType id1_1 = new IdType(resp.getEntry().get(0).getResponse().getLocation());
assertEquals("Provenance", id1_1.getResourceType());
IdType id1_2 = new IdType(resp.getEntry().get(1).getResponse().getLocation());
IdType id1_3 = new IdType(resp.getEntry().get(2).getResponse().getLocation());
IdType id1_4 = new IdType(resp.getEntry().get(3).getResponse().getLocation());
/*
* Same bundle!
*/
bundleRes = SystemProviderR4Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
resp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdType id2_1 = new IdType(resp.getEntry().get(0).getResponse().getLocation());
IdType id2_2 = new IdType(resp.getEntry().get(1).getResponse().getLocation());
IdType id2_3 = new IdType(resp.getEntry().get(2).getResponse().getLocation());
IdType id2_4 = new IdType(resp.getEntry().get(3).getResponse().getLocation());
assertNotEquals(id1_1.toVersionless(), id2_1.toVersionless());
assertEquals("Provenance", id2_1.getResourceType());
assertEquals(id1_2.toVersionless(), id2_2.toVersionless());
assertEquals(id1_3.toVersionless(), id2_3.toVersionless());
assertEquals(id1_4.toVersionless(), id2_4.toVersionless());
}
/**
* This is Gramahe's test transaction - it requires some set up in order to work
*/
// @Test
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/grahame-transaction.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
IdType id = new IdType(bundleResp.getEntry().get(0).getResponse().getLocation());
assertEquals("Patient", id.getResourceType());
assertTrue(id.hasIdPart());
assertTrue(id.isIdPartValidLong());
assertTrue(id.hasVersionIdPart());
assertTrue(id.isVersionIdPartValidLong());
}
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
} catch (InvalidRequestException e) {
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
assertEquals("processing", oo.getIssue().get(0).getCode().toCode());
}
}
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();
// } catch (InvalidRequestException e) {
// OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
// assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
// assertEquals("processing", oo.getIssue().get(0).getCode());
// }
}
@Test
public void testTransactionSearch() throws Exception {
for (int i = 0; i < 20; i++) {
Patient p = new Patient();
p.addName().setFamily("PATIENT_" + i);
myPatientDao.create(p, mySrd);
}
Bundle req = new Bundle();
req.setType(BundleType.TRANSACTION);
req.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?");
Bundle resp = ourClient.transaction().withBundle(req).execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(1, resp.getEntry().size());
Bundle respSub = (Bundle) resp.getEntry().get(0).getResource();
assertEquals("self", respSub.getLink().get(0).getRelation());
assertEquals(ourServerBase + "/Patient", respSub.getLink().get(0).getUrl());
assertEquals("next", respSub.getLink().get(1).getRelation());
assertThat(respSub.getLink().get(1).getUrl(), containsString("/fhir/context?_getpages"));
assertThat(respSub.getEntry().get(0).getFullUrl(), startsWith(ourServerBase + "/Patient/"));
assertEquals(Patient.class, respSub.getEntry().get(0).getResource().getClass());
}
@Test
public void testTransactionCount() throws Exception {
for (int i = 0; i < 20; i++) {
Patient p = new Patient();
p.addName().setFamily("PATIENT_" + i);
myPatientDao.create(p, mySrd);
}
Bundle req = new Bundle();
req.setType(BundleType.TRANSACTION);
req.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?_summary=count");
Bundle resp = ourClient.transaction().withBundle(req).execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
assertEquals(1, resp.getEntry().size());
Bundle respSub = (Bundle) resp.getEntry().get(0).getResource();
assertEquals(20, respSub.getTotal());
assertEquals(0, respSub.getEntry().size());
}
@Test
public void testTransactionCreateWithPreferHeader() throws Exception {
Patient p = new Patient();
p.setActive(true);
Bundle req;
Bundle resp;
// No prefer header
req = new Bundle();
req.setType(BundleType.TRANSACTION);
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
resp = ourClient.transaction().withBundle(req).execute();
assertEquals(null, resp.getEntry().get(0).getResource());
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
// Prefer return=minimal
mySimpleHeaderInterceptor.setHeaderName(Constants.HEADER_PREFER);
mySimpleHeaderInterceptor.setHeaderValue(Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL);
req = new Bundle();
req.setType(BundleType.TRANSACTION);
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
resp = ourClient.transaction().withBundle(req).execute();
assertEquals(null, resp.getEntry().get(0).getResource());
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
// Prefer return=representation
mySimpleHeaderInterceptor.setHeaderName(Constants.HEADER_PREFER);
mySimpleHeaderInterceptor.setHeaderValue(Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION);
req = new Bundle();
req.setType(BundleType.TRANSACTION);
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
resp = ourClient.transaction().withBundle(req).execute();
assertEquals(Patient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,312 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
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.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.*;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.rp.r4.*;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.TestUtil;
public class SystemProviderTransactionSearchR4Test extends BaseJpaR4Test {
private static RestfulServer myRestServer;
private static IGenericClient ourClient;
private static FhirContext ourCtx;
private static CloseableHttpClient ourHttpClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderTransactionSearchR4Test.class);
private static Server ourServer;
private static String ourServerBase;
private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
@SuppressWarnings("deprecation")
@After
public void after() {
myRestServer.setUseBrowserFriendlyContentTypes(true);
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
myDaoConfig.setMaximumSearchResultCountInTransaction(new DaoConfig().getMaximumSearchResultCountInTransaction());
}
@Before
public void before() {
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
ourClient.registerInterceptor(mySimpleHeaderInterceptor);
}
@Before
public void beforeStartServer() throws Exception {
if (myRestServer == null) {
PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(myPatientDao);
QuestionnaireResourceProviderR4 questionnaireRp = new QuestionnaireResourceProviderR4();
questionnaireRp.setDao(myQuestionnaireDao);
ObservationResourceProvider observationRp = new ObservationResourceProvider();
observationRp.setDao(myObservationDao);
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
organizationRp.setDao(myOrganizationDao);
RestfulServer restServer = new RestfulServer(ourCtx);
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
restServer.setPlainProviders(mySystemProvider);
int myPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(myPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourCtx = FhirContext.forR4();
restServer.setFhirContext(ourCtx);
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourHttpClient = builder.build();
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.setLogRequestAndResponse(true);
myRestServer = restServer;
}
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myRestServer.setPagingProvider(myPagingProvider);
}
private List<String> create20Patients() {
List<String> ids = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
Patient patient = new Patient();
patient.setGender(AdministrativeGender.MALE);
patient.addIdentifier().setSystem("urn:foo").setValue("A");
patient.addName().setFamily("abcdefghijklmnopqrstuvwxyz".substring(i, i+1));
String id = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getValue();
ids.add(id);
}
return ids;
}
@Test
public void testBatchWithGetHardLimitLargeSynchronous() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.BATCH);
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5");
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(1, output.getEntry().size());
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
assertEquals(5, respBundle.getEntry().size());
assertEquals(null, respBundle.getLink("next"));
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
}
@Test
public void testBatchWithGetNormalSearch() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.BATCH);
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5&_sort=name");
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(1, output.getEntry().size());
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
assertEquals(5, respBundle.getEntry().size());
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
String nextPageLink = respBundle.getLink("next").getUrl();
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
respBundle = output;
assertEquals(5, respBundle.getEntry().size());
actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
}
/**
* 30 searches in one batch! Whoa!
*/
@Test
public void testBatchWithManyGets() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.BATCH);
for (int i = 0; i < 30; i++) {
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
}
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(30, output.getEntry().size());
for (int i = 0; i < 30; i++) {
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
assertEquals(5, respBundle.getEntry().size());
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
}
}
@Test
public void testTransactionWithGetHardLimitLargeSynchronous() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5");
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(1, output.getEntry().size());
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
assertEquals(5, respBundle.getEntry().size());
assertEquals(null, respBundle.getLink("next"));
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
}
@Test
public void testTransactionWithGetNormalSearch() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5&_sort=name");
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(1, output.getEntry().size());
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
assertEquals(5, respBundle.getEntry().size());
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
String nextPageLink = respBundle.getLink("next").getUrl();
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
respBundle = output;
assertEquals(5, respBundle.getEntry().size());
actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
}
/**
* 30 searches in one Transaction! Whoa!
*/
@Test
public void testTransactionWithManyGets() throws Exception {
List<String> ids = create20Patients();
Bundle input = new Bundle();
input.setType(BundleType.TRANSACTION);
for (int i = 0; i < 30; i++) {
input
.addEntry()
.getRequest()
.setMethod(HTTPVerb.GET)
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
}
Bundle output = ourClient.transaction().withBundle(input).execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(30, output.getEntry().size());
for (int i = 0; i < 30; i++) {
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
assertEquals(5, respBundle.getEntry().size());
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
List<String> actualIds = toIds(respBundle);
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
}
}
private List<String> toIds(Bundle theRespBundle) {
ArrayList<String> retVal = new ArrayList<String>();
for (BundleEntryComponent next : theRespBundle.getEntry()) {
retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
return retVal;
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,209 @@
package ca.uhn.fhir.jpa.provider.r4;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.r4.model.Attachment;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.UriType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderR4Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testUploadSct() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
}
@Test
public void testUploadLoinc() throws Exception {
byte[] packageBytes = createLoincZip();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
/*
* Try uploading a second time
*/
//@formatter:off
respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
//@formatter:on
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
}
@Test
public void testUploadSctLocalFile() throws Exception {
byte[] packageBytes = createSctZip();
File tempFile = File.createTempFile("tmp", ".zip");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(packageBytes);
fos.close();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
}
@Test
public void testUploadInvalidUrl() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL + "FOO"))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Unknown URL: http://snomed.info/sctFOO", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadMissingUrl() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "package", new Attachment().setData(packageBytes))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadMissingPackage() throws Exception {
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage());
}
//@formatter:on
}
private byte[] createSctZip() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(bos);
List<String> inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt","sct2_Concept_Full-en_INT_20160131.txt","sct2_Description_Full-en_INT_20160131.txt","sct2_Identifier_Full_INT_20160131.txt","sct2_Relationship_Full_INT_20160131.txt","sct2_StatedRelationship_Full_INT_20160131.txt","sct2_TextDefinition_Full-en_INT_20160131.txt");
for (String nextName : inputNames) {
zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName));
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName)));
}
zos.close();
byte[] packageBytes = bos.toByteArray();
return packageBytes;
}
private byte[] createLoincZip() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(bos);
zos.putNextEntry(new ZipEntry("loinc.csv"));
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv")));
zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"));
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")));
zos.close();
byte[] packageBytes = bos.toByteArray();
return packageBytes;
}
}

View File

@ -0,0 +1,97 @@
package ca.uhn.fhir.jpa.search.r4;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertThat;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Patient;
import org.junit.*;
import org.springframework.test.util.AopTestUtils;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.util.TestUtil;
public class PagingMultinodeProviderR4Test extends BaseResourceProviderR4Test {
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false);
}
@Override
public void before() throws Exception {
super.before();
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myDaoConfig.setAllowMultipleDelete(true);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
}
@Test
public void testSearch() {
{
for (int i = 0; i < 100; i++) {
Patient patient = new Patient();
String id = "A" + leftPad(Integer.toString(i), 3, '0');
patient.setId(id);
patient.addIdentifier().setSystem("urn:system").setValue("A" + i);
patient.addName().setFamily(id);
myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
}
}
Bundle found;
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(50);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(10);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(true);
found = ourClient
.search()
.forResource(Patient.class)
.sort().ascending(Patient.SP_FAMILY)
.count(10)
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A000", "Patient/A001", "Patient/A002", "Patient/A003", "Patient/A004", "Patient/A005", "Patient/A006", "Patient/A007", "Patient/A008", "Patient/A009"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A010", "Patient/A011", "Patient/A012", "Patient/A013", "Patient/A014", "Patient/A015", "Patient/A016", "Patient/A017", "Patient/A018", "Patient/A019"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A020", "Patient/A021", "Patient/A022", "Patient/A023", "Patient/A024", "Patient/A025", "Patient/A026", "Patient/A027", "Patient/A028", "Patient/A029"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A030", "Patient/A031", "Patient/A032", "Patient/A033", "Patient/A034", "Patient/A035", "Patient/A036", "Patient/A037", "Patient/A038", "Patient/A039"));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -0,0 +1,94 @@
package ca.uhn.fhir.jpa.subscription.r4;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
public class FhirR4Util {
public static final String LPI_CODESYSTEM = "http://cognitivemedicine.com/lpi";
public static final String LPI_CODE = "LPI-FHIR";
public static Subscription createSubscription(String criteria, String payload, String endpoint, IGenericClient client) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
subscription.setCriteria(criteria);
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
channel.setPayload(payload);
channel.setEndpoint(endpoint);
subscription.setChannel(channel);
MethodOutcome methodOutcome = client.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
return subscription;
}
public static Observation getSnomedObservation() {
Coding snomedCoding = new Coding();
snomedCoding.setSystem("SNOMED-CT");
snomedCoding.setCode("1000000050");
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation.getCode().addCoding(snomedCoding);
return observation;
}
public static Observation getLoincObservation() {
Coding snomedCoding = new Coding();
snomedCoding.setSystem("http://loinc.org");
snomedCoding.setCode("55284-4");
snomedCoding.setDisplay("Blood Pressure");
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation.getCode().addCoding(snomedCoding);
return observation;
}
/**
* Create a patient object for the test
*
* @return
*/
public static Patient getPatient() {
String patientId = "1";
Patient patient = new Patient();
patient.setGender(Enumerations.AdministrativeGender.MALE);
Identifier identifier = patient.addIdentifier();
identifier.setValue(patientId);
identifier.setSystem(LPI_CODESYSTEM);
IBaseMetaType meta = patient.getMeta();
IBaseCoding tag = meta.addTag();
tag.setCode(LPI_CODE);
tag.setSystem(LPI_CODESYSTEM);
setTag(patient);
return patient;
}
/**
* Set the tag for a resource
*
* @param resource
*/
public static void setTag(IBaseResource resource) {
IBaseMetaType meta = resource.getMeta();
IBaseCoding tag = meta.addTag();
tag.setCode(LPI_CODE);
tag.setSystem(LPI_CODESYSTEM);
}
}

View File

@ -0,0 +1,171 @@
package ca.uhn.fhir.jpa.subscription.r4;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.net.URI;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.slf4j.Logger;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
/**
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
* subscription
* <p>
* Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdR4Test for
* a test that returns the xml of the observation
* <p>
* To execute the following test, execute it the following way:
* 0. execute 'clean' test
* 1. Execute the 'createPatient' test
* 2. Update the patient id static variable
* 3. Execute the 'createSubscription' test
* 4. Update the subscription id static variable
* 5. Execute the 'attachWebSocket' test
* 6. Execute the 'sendObservation' test
* 7. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
*/
public class FhirSubscriptionWithCriteriaR4Test extends BaseResourceProviderR4Test {
private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithCriteriaR4Test.class);
private String myPatientId;
private String mySubscriptionId;
private WebSocketClient myWebSocketClient;
private SocketImplementation mySocketImplementation;
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
}
@Override
@Before
public void before() throws Exception {
super.before();
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPollDelay(0L);
/*
* Create patient
*/
Patient patient = FhirR4Util.getPatient();
MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
myPatientId = methodOutcome.getId().getIdPart();
/*
* Create subscription
*/
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
// subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
subscription.setCriteria("Observation?code=SNOMED-CT|82313006&_format=xml");
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
channel.setPayload("application/json");
subscription.setChannel(channel);
methodOutcome = ourClient.create().resource(subscription).execute();
mySubscriptionId = methodOutcome.getId().getIdPart();
/*
* Attach websocket
*/
myWebSocketClient = new WebSocketClient();
mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
myWebSocketClient.start();
URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/r4");
ClientUpgradeRequest request = new ClientUpgradeRequest();
ourLog.info("Connecting to : {}", echoUri);
Future<Session> connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request);
Session session = connection.get(2, TimeUnit.SECONDS);
ourLog.info("Connected to WS: {}", session.isOpen());
}
@After
public void afterCloseWebsocket() throws Exception {
ourLog.info("Shutting down websocket client");
myWebSocketClient.stop();
}
@Test
public void createObservation() throws Exception {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode("82313006");
coding.setSystem("SNOMED-CT");
Reference reference = new Reference();
reference.setReference("Patient/" + myPatientId);
observation.setSubject(reference);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
String observationId = methodOutcome2.getId().getIdPart();
observation.setId(observationId);
ourLog.info("Observation id generated by server is: " + observationId);
int changes = mySubscriptionDao.pollForNewUndeliveredResources();
ourLog.info("Polling showed {}", changes);
assertEquals(1, changes);
Thread.sleep(2000);
ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
}
@Test
public void createObservationThatDoesNotMatch() throws Exception {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode("8231");
coding.setSystem("SNOMED-CT");
Reference reference = new Reference();
reference.setReference("Patient/" + myPatientId);
observation.setSubject(reference);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
String observationId = methodOutcome2.getId().getIdPart();
observation.setId(observationId);
ourLog.info("Observation id generated by server is: " + observationId);
int changes = mySubscriptionDao.pollForNewUndeliveredResources();
ourLog.info("Polling showed {}", changes);
assertEquals(0, changes);
Thread.sleep(2000);
ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
}
}

View File

@ -0,0 +1,169 @@
package ca.uhn.fhir.jpa.subscription.r4;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.net.URI;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.slf4j.Logger;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.subscription.SocketImplementation;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
/**
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
* subscription
* <p>
* Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdR4Test for
* a test that returns the xml of the observation
* <p>
* To execute the following test, execute it the following way:
* 0. execute 'clean' test
* 1. Execute the 'createPatient' test
* 2. Update the patient id static variable
* 3. Execute the 'createSubscription' test
* 4. Update the subscription id static variable
* 5. Execute the 'attachWebSocket' test
* 6. Execute the 'sendObservation' test
* 7. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
*/
public class FhirSubscriptionWithSubscriptionIdR4Test extends BaseResourceProviderR4Test {
private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdR4Test.class);
private String myPatientId;
private String mySubscriptionId;
private WebSocketClient myWebSocketClient;
private SocketImplementation mySocketImplementation;
@After
public void after() throws Exception {
super.after();
myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
}
@Before
public void before() throws Exception {
super.before();
myDaoConfig.setSubscriptionEnabled(true);
myDaoConfig.setSubscriptionPollDelay(0L);
/*
* Create patient
*/
Patient patient = FhirR4Util.getPatient();
MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
myPatientId = methodOutcome.getId().getIdPart();
/*
* Create subscription
*/
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
// subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
subscription.setCriteria("Observation?code=SNOMED-CT|82313006");
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
channel.setPayload("application/json");
subscription.setChannel(channel);
methodOutcome = ourClient.create().resource(subscription).execute();
mySubscriptionId = methodOutcome.getId().getIdPart();
/*
* Attach websocket
*/
myWebSocketClient = new WebSocketClient();
mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
myWebSocketClient.start();
URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/r4");
ClientUpgradeRequest request = new ClientUpgradeRequest();
ourLog.info("Connecting to : {}", echoUri);
Future<Session> connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request);
Session session = connection.get(2, TimeUnit.SECONDS);
ourLog.info("Connected to WS: {}", session.isOpen());
}
@After
public void afterCloseWebsocket() throws Exception {
ourLog.info("Shutting down websocket client");
myWebSocketClient.stop();
}
@Test
public void createObservation() throws Exception {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode("82313006");
coding.setSystem("SNOMED-CT");
Reference reference = new Reference();
reference.setReference("Patient/" + myPatientId);
observation.setSubject(reference);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
String observationId = methodOutcome2.getId().getIdPart();
observation.setId(observationId);
ourLog.info("Observation id generated by server is: " + observationId);
int changes = mySubscriptionDao.pollForNewUndeliveredResources();
ourLog.info("Polling showed {}", changes);
assertEquals(1, changes);
Thread.sleep(2000);
ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
}
@Test
public void createObservationThatDoesNotMatch() throws Exception {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode("8231");
coding.setSystem("SNOMED-CT");
Reference reference = new Reference();
reference.setReference("Patient/" + myPatientId);
observation.setSubject(reference);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
String observationId = methodOutcome2.getId().getIdPart();
observation.setId(observationId);
ourLog.info("Observation id generated by server is: " + observationId);
int changes = mySubscriptionDao.pollForNewUndeliveredResources();
ourLog.info("Polling showed {}", changes);
assertEquals(0, changes);
Thread.sleep(2000);
ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
}
}

View File

@ -0,0 +1,362 @@
package ca.uhn.fhir.jpa.subscription.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.rest.api.Constants;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.*;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* Test the rest-hook subscriptions
*/
public class RestHookTestR4Test extends BaseResourceProviderR4Test {
private static List<String> ourContentTypes = new ArrayList<String>();
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
private static int ourListenerPort;
private static RestfulServer ourListenerRestServer;
private static Server ourListenerServer;
private static String ourListenerServerBase;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestR4Test.class);
private static List<Observation> ourUpdatedObservations = Lists.newArrayList();
@After
public void afterUnregisterRestHookListener() {
myDaoConfig.setAllowMultipleDelete(true);
ourLog.info("Deleting all subscriptions");
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
ourLog.info("Done deleting all subscriptions");
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
}
@Before
public void beforeRegisterRestHookListener() {
ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
}
@Before
public void beforeReset() {
ourCreatedObservations.clear();
ourUpdatedObservations.clear();
ourContentTypes.clear();
}
@Test
public void testRestHookSubscriptionInvalidCriteria() throws Exception {
String payload = "application/xml";
String criteria1 = "Observation?codeeeee=SNOMED-CT";
try {
createSubscription(criteria1, payload, ourListenerServerBase);
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Invalid criteria: Failed to parse match URL[Observation?codeeeee=SNOMED-CT] - Resource type Observation does not have a parameter with name: codeeeee", e.getMessage());
}
}
private Subscription createSubscription(String theCriteria, String thePayload, String theEndpoint) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setCriteria(theCriteria);
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
channel.setPayload(thePayload);
channel.setEndpoint(theEndpoint);
subscription.setChannel(channel);
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
return subscription;
}
private Observation sendObservation(String code, String system) {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code);
coding.setSystem(system);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
String observationId = methodOutcome.getId().getIdPart();
observation.setId(observationId);
return observation;
}
@Test
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
String payload = "application/fhir+json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
createSubscription(criteria1, payload, ourListenerServerBase);
createSubscription(criteria2, payload, ourListenerServerBase);
sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationJson() throws Exception {
String payload = "application/json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept = new CodeableConcept();
observation3.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code + "111");
coding.setSystem("SNOMED-CT");
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept1 = new CodeableConcept();
observation3a.setCode(codeableConcept1);
Coding coding1 = codeableConcept1.addCoding();
coding1.setCode(code);
coding1.setSystem("SNOMED-CT");
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@Test
public void testRestHookSubscriptionApplicationXmlJson() throws Exception {
String payload = "application/fhir+xml";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationXml() throws Exception {
String payload = "application/xml";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept = new CodeableConcept();
observation3.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code + "111");
coding.setSystem("SNOMED-CT");
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept1 = new CodeableConcept();
observation3a.setCode(codeableConcept1);
Coding coding1 = codeableConcept1.addCoding();
coding1.setCode(code);
coding1.setSystem("SNOMED-CT");
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = RandomServerPortProvider.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
ObservationListener obsListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(obsListener);
ourListenerServer = new Server(ourListenerPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourListenerRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourListenerServer.setHandler(proxyHandler);
ourListenerServer.start();
}
@AfterClass
public static void stopListenerServer() throws Exception {
ourListenerServer.stop();
}
public static class ObservationListener implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
ourLog.info("Received Listener Create");
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
ourCreatedObservations.add(theObservation);
return new MethodOutcome(new IdType("Observation/1"), true);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Observation.class;
}
@Update
public MethodOutcome update(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
ourLog.info("Received Listener Update");
ourUpdatedObservations.add(theObservation);
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
return new MethodOutcome(new IdType("Observation/1"), false);
}
}
}

View File

@ -0,0 +1,299 @@
package ca.uhn.fhir.jpa.subscription.r4;
import static org.junit.Assert.*;
import java.util.List;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.*;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
/**
* Test the rest-hook subscriptions
*/
public class RestHookTestWithInterceptorRegisteredToDaoConfigR4Test extends BaseResourceProviderR4Test {
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
private static int ourListenerPort;
private static RestfulServer ourListenerRestServer;
private static Server ourListenerServer;
private static String ourListenerServerBase;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestWithInterceptorRegisteredToDaoConfigR4Test.class);
private static List<Observation> ourUpdatedObservations = Lists.newArrayList();
@Override
protected boolean shouldLogClient() {
return false;
}
@After
public void afterUnregisterRestHookListener() {
myDaoConfig.setAllowMultipleDelete(true);
ourLog.info("Deleting all subscriptions");
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
ourLog.info("Done deleting all subscriptions");
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.getInterceptors().remove(ourRestHookSubscriptionInterceptor);
}
@Before
public void beforeRegisterRestHookListener() {
myDaoConfig.getInterceptors().add(ourRestHookSubscriptionInterceptor);
}
@Before
public void beforeReset() {
ourCreatedObservations.clear();
ourUpdatedObservations.clear();
}
private Subscription createSubscription(String criteria, String payload, String endpoint) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setCriteria(criteria);
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
channel.setPayload(payload);
channel.setEndpoint(endpoint);
subscription.setChannel(channel);
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
return subscription;
}
private Observation sendObservation(String code, String system) {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code);
coding.setSystem(system);
observation.setStatus(Observation.ObservationStatus.FINAL);
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
String observationId = methodOutcome.getId().getIdPart();
observation.setId(observationId);
return observation;
}
@Test
public void testRestHookSubscriptionJson() throws Exception {
String payload = "application/json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept = new CodeableConcept();
observation3.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code + "111");
coding.setSystem("SNOMED-CT");
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept1 = new CodeableConcept();
observation3a.setCode(codeableConcept1);
Coding coding1 = codeableConcept1.addCoding();
coding1.setCode(code);
coding1.setSystem("SNOMED-CT");
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@Test
public void testRestHookSubscriptionXml() throws Exception {
String payload = "application/xml";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
Thread.sleep(500);
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept = new CodeableConcept();
observation3.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code + "111");
coding.setSystem("SNOMED-CT");
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
CodeableConcept codeableConcept1 = new CodeableConcept();
observation3a.setCode(codeableConcept1);
Coding coding1 = codeableConcept1.addCoding();
coding1.setCode(code);
coding1.setSystem("SNOMED-CT");
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = RandomServerPortProvider.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
ObservationListener obsListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(obsListener);
ourListenerServer = new Server(ourListenerPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourListenerRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourListenerServer.setHandler(proxyHandler);
ourListenerServer.start();
}
@AfterClass
public static void stopListenerServer() throws Exception {
ourListenerServer.stop();
}
public static class ObservationListener implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Observation theObservation) {
ourLog.info("Received Listener Create");
ourCreatedObservations.add(theObservation);
return new MethodOutcome(new IdType("Observation/1"), true);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Observation.class;
}
@Update
public MethodOutcome update(@ResourceParam Observation theObservation) {
ourLog.info("Received Listener Update");
ourUpdatedObservations.add(theObservation);
return new MethodOutcome(new IdType("Observation/1"), false);
}
}
}

View File

@ -4,6 +4,9 @@ import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.IWorkerContext;
@ -109,4 +112,22 @@ public class ObjectConverter {
return c;
}
public static Identifier readAsIdentifier(Element item) {
Identifier r = new Identifier();
r.setSystem(item.getNamedChildValue("system"));
r.setValue(item.getNamedChildValue("value"));
return r;
}
public static Reference readAsReference(Element item) {
Reference r = new Reference();
r.setDisplay(item.getNamedChildValue("display"));
r.setReference(item.getNamedChildValue("reference"));
List<Element> identifier = item.getChildrenByName("identifier");
if (identifier.isEmpty() == false) {
r.setIdentifier(readAsIdentifier(identifier.get(0)));
}
return r;
}
}

View File

@ -173,7 +173,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
retVal.setPublisher(myPublisher);
retVal.setDateElement(conformanceDate());
retVal.setFhirVersion(FhirVersionEnum.DSTU3.getFhirVersionString());
retVal.setFhirVersion(FhirVersionEnum.R4.getFhirVersionString());
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big
// effort since the parser
// needs to be modified to actually allow it

View File

@ -2,16 +2,14 @@ package org.hl7.fhir.r4.model;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.DataFormatException;
public abstract class BaseDateTimeType extends PrimitiveType<Date> {

View File

@ -29,13 +29,12 @@ POSSIBILITY OF SUCH DAMAGE.
package org.hl7.fhir.r4.model;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.*;
import java.util.zip.DataFormatException;
import org.apache.commons.lang3.time.DateUtils;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/**

View File

@ -29,18 +29,14 @@ POSSIBILITY OF SUCH DAMAGE.
package org.hl7.fhir.r4.model;
import java.util.Calendar;
/**
* Primitive type "date" in FHIR: any day in a gregorian calendar
*/
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.*;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/**

View File

@ -31,11 +31,10 @@ POSSIBILITY OF SUCH DAMAGE.
*/
package org.hl7.fhir.r4.model;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.*;
import java.util.zip.DataFormatException;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/**

View File

@ -30,16 +30,14 @@ package org.hl7.fhir.r4.model;
*/
// Generated on Sat, Jul 8, 2017 23:19+1000 for FHIR v3.1.0
import java.util.Date;
import java.util.List;
import java.util.*;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ChildOrder;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Block;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.ICompositeType;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.*;
/**
* A time period defined by a start and end date and optionally time.
*/

View File

@ -1,62 +0,0 @@
package org.hl7.fhir.r4.model;
import java.util.Calendar;
import java.util.Date;
import org.apache.commons.lang3.time.DateUtils;
public enum TemporalPrecisionEnum {
YEAR(Calendar.YEAR) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addYears(theInput, theAmount);
}
},
MONTH(Calendar.MONTH) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addMonths(theInput, theAmount);
}
},
DAY(Calendar.DATE) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addDays(theInput, theAmount);
}
},
MINUTE(Calendar.MINUTE) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addMinutes(theInput, theAmount);
}
},
SECOND(Calendar.SECOND) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addSeconds(theInput, theAmount);
}
},
MILLI(Calendar.MILLISECOND) {
@Override
public Date add(Date theInput, int theAmount) {
return DateUtils.addMilliseconds(theInput, theAmount);
}
},
;
private int myCalendarConstant;
TemporalPrecisionEnum(int theCalendarConstant) {
myCalendarConstant = theCalendarConstant;
}
public abstract Date add(Date theInput, int theAmount);
public int getCalendarConstant() {
return myCalendarConstant;
}
}

View File

@ -166,7 +166,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
throws FHIRException {
def.checkNoModifiers("Code in Code System", "expanding");
// def.checkNoModifiers("Code in Code System", "expanding");
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);

View File

@ -1,54 +1,23 @@
package org.hl7.fhir.r4.utils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.elementmodel.Element;
import org.hl7.fhir.r4.elementmodel.ObjectConverter;
import org.hl7.fhir.r4.model.Base;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.DecimalType;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r4.model.ExpressionNode;
import org.hl7.fhir.r4.model.ExpressionNode.CollectionStatus;
import org.hl7.fhir.r4.model.ExpressionNode.Function;
import org.hl7.fhir.r4.model.ExpressionNode.Kind;
import org.hl7.fhir.r4.model.ExpressionNode.Operation;
import org.hl7.fhir.r4.model.ExpressionNode.SourceLocation;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Property;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ExpressionNode.*;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.model.TemporalPrecisionEnum;
import org.hl7.fhir.r4.model.TimeType;
import org.hl7.fhir.r4.model.TypeDetails;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.exceptions.UcumException;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.ucum.Decimal;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ElementUtil;

View File

@ -149,7 +149,7 @@ public class ClientR4Test {
HttpPost post = (HttpPost) capt.getValue();
assertThat(IOUtils.toString(post.getEntity().getContent(), Charsets.UTF_8), StringContains.containsString("<Patient"));
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals("200", response.getId().getVersionIdPart());
}
@ -1038,7 +1038,7 @@ public class ClientR4Test {
assertThat(IOUtils.toString(post.getEntity().getContent(), Charsets.UTF_8), StringContains.containsString("<Patient"));
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getId().getVersionIdPart());
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
}
/**
@ -1058,15 +1058,14 @@ public class ClientR4Test {
when(myHttpResponse.getAllHeaders()).thenReturn(toHeaderArray(Constants.HEADER_LOCATION, "http://example.com/fhir/Patient/100/_history/200"));
ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
client.updatePatient(new IdType("Patient/100/_history/200"), patient);
MethodOutcome resp = client.updatePatient(new IdType("Patient/100/_history/200"), patient);
assertNull(resp.getResource());
assertNull(resp.getOperationOutcome());
assertEquals(HttpPut.class, capt.getValue().getClass());
HttpPut post = (HttpPut) capt.getValue();
assertEquals("http://foo/Patient/100", post.getURI().toASCIIString());
Header h = post.getFirstHeader(Constants.HEADER_LOCATION);
assertEquals("Patient/100/_history/200", h.getValue());
}
@Test(expected = ResourceVersionConflictException.class)
@ -1105,7 +1104,6 @@ public class ClientR4Test {
HttpPut post = (HttpPut) capt.getValue();
assertThat(post.getURI().toASCIIString(), StringEndsWith.endsWith("/Patient/100"));
assertThat(IOUtils.toString(post.getEntity().getContent(), Charsets.UTF_8), StringContains.containsString("<Patient"));
assertThat(post.getFirstHeader(Constants.HEADER_LOCATION).getValue(), StringEndsWith.endsWith("Patient/100/_history/200"));
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getId().getVersionIdPart());
}
@ -1204,12 +1202,12 @@ public class ClientR4Test {
// Older resource
{
BundleEntryComponent olderEntry = response.getEntry().get(0);
assertEquals("http://acme.com/Patient/111", olderEntry.getId());
assertEquals("http://acme.com/Patient/111", olderEntry.getResource().getId());
}
// Newer resource
{
BundleEntryComponent newerEntry = response.getEntry().get(1);
assertEquals("http://acme.com/Patient/222", newerEntry.getId());
assertEquals("http://acme.com/Patient/222", newerEntry.getResource().getId());
}
}

View File

@ -109,7 +109,25 @@ public class ClientServerValidationDstu1Test {
}
}
@AfterClass
@Test
public void testServerReturnsRightVersionDstu() throws Exception {
CapabilityStatement conf = new CapabilityStatement();
conf.setFhirVersion("3.1.0");
String msg = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
myCtx.newRestfulGenericClient("http://foo").forceConformanceCheck();
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}

View File

@ -458,13 +458,6 @@ public class GenericClientTest {
lm.setTimeZoneZulu(true);
assertEquals("1995-11-15T04:58:08.000Z", lm.getValueAsString());
List<Coding> tags = response.getMeta().getTag();
assertNotNull(tags);
assertEquals(1, tags.size());
assertEquals("http://hl7.org/fhir/tag", tags.get(0).getSystem());
assertEquals("http://foo/tagdefinition.html", tags.get(0).getCode());
assertEquals("Some tag", tags.get(0).getDisplay());
}
@Test

View File

@ -81,7 +81,7 @@ public class LoggingInterceptorTest {
public boolean matches(final Object argument) {
String formattedMessage = ((LoggingEvent) argument).getFormattedMessage();
System.out.println("Verifying: " + formattedMessage);
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/xml+fhir;charset=utf-8".toLowerCase());
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/fhir+xml;charset=utf-8".toLowerCase());
}
}));
@ -96,7 +96,7 @@ public class LoggingInterceptorTest {
public boolean matches(final Object argument) {
String formattedMessage = ((LoggingEvent) argument).getFormattedMessage();
System.out.println("Verifying: " + formattedMessage);
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/xml+fhir;charset=utf-8".toLowerCase());
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/fhir+xml;charset=utf-8".toLowerCase());
}
}));

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,140 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hamcrest.Matchers;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Encounter;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.util.TestUtil;
public class SearchClientTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchClientTest.class);
private FhirContext ourCtx;
private HttpClient ourHttpClient;
private HttpResponse ourHttpResponse;
@Before
public void before() {
ourCtx = FhirContext.forR4();
ourHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(ourHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testPostOnLongParamsList() throws Exception {
String resp = createBundle();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(resp), Charset.forName("UTF-8")));
ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
Set<Include> includes = new HashSet<Include>();
includes.add(new Include("one"));
includes.add(new Include("two"));
TokenOrListParam params = new TokenOrListParam();
for (int i = 0; i < 1000; i++) {
params.add(new TokenParam("system", "value"));
}
List<Encounter> found = client.searchByList(params, includes);
assertEquals(1, found.size());
Encounter encounter = found.get(0);
assertNotNull(encounter.getSubject().getReference());
HttpUriRequest value = capt.getValue();
assertTrue("Expected request of type POST on long params list", value instanceof HttpPost);
HttpPost post = (HttpPost) value;
String body = IOUtils.toString(post.getEntity().getContent());
ourLog.info(body);
assertThat(body, Matchers.containsString("_include=one"));
assertThat(body, Matchers.containsString("_include=two"));
}
@Test
public void testReturnTypedList() throws Exception {
String resp = createBundle();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(resp), Charset.forName("UTF-8")));
ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
List<Encounter> found = client.search();
assertEquals(1, found.size());
Encounter encounter = found.get(0);
assertNotNull(encounter.getSubject().getReference());
}
private String createBundle() {
Bundle bundle = new Bundle();
Encounter enc = new Encounter();
enc.getSubject().setReference("Patient/1");
bundle.addEntry().setResource(enc);
String retVal = ourCtx.newXmlParser().encodeResourceToString(bundle);
return retVal;
}
private interface ITestClient extends IBasicClient {
@Search
List<Encounter> search();
@Search
List<Encounter> searchByList(@RequiredParam(name = Encounter.SP_IDENTIFIER) TokenOrListParam tokenOrListParam, @IncludeParam Set<Include> theIncludes) throws BaseServerResponseException;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -80,7 +80,7 @@ public class SortClientTest {
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient?name=hello&_sort%3Adesc=given&_sort%3Aasc=family", get.getURI().toString());
assertEquals("http://foo/Patient?name=hello&_sort=-given%2Cfamily", get.getURI().toString());
}
private String createBundle() {

View File

@ -66,9 +66,9 @@ public class IncludeTest {
assertEquals(3, bundle.getEntry().size());
assertEquals(new IdType("Patient/p1"), bundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
assertEquals(new IdType("Patient/p2"), bundle.getEntry().get(1).getResource().getIdElement().toUnqualifiedVersionless().getValue());
assertEquals(new IdType("Organization/o1"), bundle.getEntry().get(2).getResource().getIdElement().toUnqualifiedVersionless().getValue());
assertEquals(("Patient/p1"), bundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
assertEquals(("Patient/p2"), bundle.getEntry().get(1).getResource().getIdElement().toUnqualifiedVersionless().getValue());
assertEquals(("Organization/o1"), bundle.getEntry().get(2).getResource().getIdElement().toUnqualifiedVersionless().getValue());
Patient p1 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, p1.getContained().size());

View File

@ -31,7 +31,7 @@ public class PropertyTest {
@Before
public void setUp() throws IOException {
final String sdString = IOUtils.toString(getClass().getResourceAsStream("/customPatientSd.xml"), StandardCharsets.UTF_8);
final String sdString = IOUtils.toString(PropertyTest.class.getResourceAsStream("/customPatientSd.xml"), StandardCharsets.UTF_8);
final IParser parser = ourCtx.newXmlParser();
sd = parser.parseResource(StructureDefinition.class, sdString);
workerContext = new HapiWorkerContext(ourCtx, new DefaultProfileValidationSupport());

View File

@ -8,6 +8,7 @@ import java.io.InputStream;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.exceptions.*;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.IWorkerContext;
@ -333,15 +334,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
source = Source.InstanceValidator;
}
public InstanceValidator(ValidationEngine engine) {
super();
this.context = engine.getContext();
fpe = engine.getFpe();
this.externalHostServices = fpe.getHostServices();
fpe.setHostServices(new ValidatorHostServices());
source = Source.InstanceValidator;
}
@Override
public boolean isNoInvariantChecks() {
return noInvariantChecks;
@ -1427,16 +1419,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
private void checkReference(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException, IOException {
String ref = null;
try {
// Do this inside a try because invalid instances might provide more than one reference.
ref = element.getNamedChildValue("reference");
} catch (Error e) {
}
Reference reference = ObjectConverter.readAsReference(element);
String ref = reference.getReference();
if (Utilities.noString(ref)) {
// todo - what should we do in this case?
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference should have a display");
if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) {
warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), "A Reference without an actual reference or identifier should have a display");
}
return;
}

View File

@ -1,21 +0,0 @@
package org.hl7.fhir.r4.validation;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
/**
* Placeholdr class only, do not use
*/
public class ValidationEngine {
public IWorkerContext getContext() {
// TODO Auto-generated method stub
return null;
}
public FHIRPathEngine getFpe() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -43,7 +43,7 @@ import ca.uhn.fhir.util.TestUtil;
public class FhirInstanceValidatorR4Test {
private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport();
private static FhirContext ourCtx = FhirContext.forDstu3();
private static FhirContext ourCtx = FhirContext.forR4();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidatorR4Test.class);
private FhirInstanceValidator myInstanceVal;
private IValidationSupport myMockSupport;
@ -157,20 +157,6 @@ public class FhirInstanceValidatorR4Test {
ourLog.info("Validated the following:\n{}", ids);
}
/**
* FHIRPathEngine was throwing Error...
*/
@Test
public void testValidateCrucibleCarePlan() throws Exception {
org.hl7.fhir.r4.model.Bundle bundle;
String name = "profiles-resources";
ourLog.info("Uploading " + name);
String vsContents;
vsContents = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/crucible-condition.xml"), "UTF-8");
ValidationResult output = myVal.validateWithResult(vsContents);
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
}
@Test
@Ignore
@ -201,7 +187,7 @@ public class FhirInstanceValidatorR4Test {
}
@Test
@Test @Ignore
public void testValidateDocument() throws Exception {
String vsContents = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/sample-document.xml"), "UTF-8");
@ -367,6 +353,7 @@ public class FhirInstanceValidatorR4Test {
}
@Test
@Ignore
public void testValidateBigRawJsonResource() throws Exception {
InputStream stream = FhirInstanceValidatorR4Test.class.getResourceAsStream("/conformance.json.gz");
stream = new GZIPInputStream(stream);
@ -389,6 +376,7 @@ public class FhirInstanceValidatorR4Test {
}
@Test
@Ignore
public void testValidateQuestionnaireResponse() throws IOException {
String input = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/qr_jon.xml"));
@ -520,6 +508,7 @@ public class FhirInstanceValidatorR4Test {
}
@Test
@Ignore
public void testValidateRawJsonResourceFromExamples() throws Exception {
// @formatter:off
String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/testscript-search.json"));
@ -598,7 +587,7 @@ public class FhirInstanceValidatorR4Test {
ourLog.info(output.getMessages().get(0).getLocationString());
ourLog.info(output.getMessages().get(0).getMessage());
assertEquals("/f:Patient", output.getMessages().get(0).getLocationString());
assertEquals("Undefined element 'foo\"", output.getMessages().get(0).getMessage());
assertEquals("Undefined element 'foo'", output.getMessages().get(0).getMessage());
}
@Test

View File

@ -52,8 +52,12 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp
IFhirResourceDaoValueSet<ca.uhn.fhir.model.dstu2.resource.ValueSet, ca.uhn.fhir.model.dstu2.composite.CodingDt, ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt>
#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ValueSet' )
IFhirResourceDaoValueSet<org.hl7.fhir.dstu3.model.ValueSet, org.hl7.fhir.dstu3.model.Coding, org.hl7.fhir.dstu3.model.CodeableConcept>
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ValueSet' )
IFhirResourceDaoValueSet<org.hl7.fhir.r4.model.ValueSet, org.hl7.fhir.r4.model.Coding, org.hl7.fhir.r4.model.CodeableConcept>
#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'CodeSystem' )
IFhirResourceDaoCodeSystem<org.hl7.fhir.dstu3.model.CodeSystem, org.hl7.fhir.dstu3.model.Coding, org.hl7.fhir.dstu3.model.CodeableConcept>
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'CodeSystem' )
IFhirResourceDaoCodeSystem<org.hl7.fhir.r4.model.CodeSystem, org.hl7.fhir.r4.model.Coding, org.hl7.fhir.r4.model.CodeableConcept>
#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter'))
IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}>
#else