Work on adding conditional HTTP operations to client and JPA server

This commit is contained in:
jamesagnew 2015-02-23 09:08:59 -05:00
parent f76a875bc4
commit 315ad439e4
46 changed files with 4083 additions and 2476 deletions

View File

@ -72,6 +72,7 @@ import ca.uhn.fhir.validation.FhirValidator;
public class FhirContext {
private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class);
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private HapiLocalizer myLocalizer = new HapiLocalizer();
@ -81,6 +82,7 @@ public class FhirContext {
private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private final IFhirVersion myVersion;
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
/**
@ -124,6 +126,8 @@ public class FhirContext {
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures"));
}
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
scanResourceTypes(toElementList(theResourceTypes));
}
@ -457,6 +461,10 @@ public class FhirContext {
return new FhirContext(FhirVersionEnum.DSTU2);
}
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
retVal.add(theResourceType);
@ -475,8 +483,4 @@ public class FhirContext {
return retVal;
}
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
}

View File

@ -216,7 +216,7 @@ class ModelScanner {
long time = System.currentTimeMillis() - start;
int size = myClassToElementDefinitions.size() - startSize;
ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", size, time);
ourLog.debug("Done scanning FHIR library, found {} model entries in {}ms", size, time);
}
/**

View File

@ -113,6 +113,18 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String> {
this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
}
/**
* Constructor
*
* @param theResourceType
* The resource type (e.g. "Patient")
* @param theIdPart
* The ID (e.g. "123")
*/
public IdDt(String theResourceType, Long theIdPart) {
this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
}
/**
* Constructor
*
@ -515,6 +527,13 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String> {
return theIdPart.toPlainString();
}
private static String toPlainStringWithNpeThrowIfNeeded(Long theIdPart) {
if (theIdPart == null) {
throw new NullPointerException("Long ID can not be null");
}
return theIdPart.toString();
}
@Override
public boolean isEmpty() {
return isBlank(getValue());

View File

@ -146,8 +146,9 @@ public class MethodOutcome {
* If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
* whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
*/
public void setCreated(Boolean theCreated) {
public MethodOutcome setCreated(Boolean theCreated) {
myCreated = theCreated;
return this;
}
/**

View File

@ -182,8 +182,13 @@ public abstract class BaseClient {
}
try {
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatusLine().getStatusCode()) {
mimeType = null;
} else {
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct != null ? ct.getMimeType() : null;
mimeType = ct != null ? ct.getMimeType() : null;
}
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
@ -398,7 +403,9 @@ public abstract class BaseClient {
charset = ct.getCharset();
}
if (charset == null) {
if (Constants.STATUS_HTTP_204_NO_CONTENT != theResponse.getStatusLine().getStatusCode()) {
ourLog.warn("Response did not specify a charset.");
}
charset = Charset.forName("UTF-8");
}

View File

@ -59,6 +59,9 @@ public abstract class BaseHttpClientInvocation {
public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding);
protected static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
if (theExtraParams==null) {
return;
}
boolean first = theWithQuestionMark;
if (theExtraParams != null && theExtraParams.isEmpty() == false) {

View File

@ -56,10 +56,14 @@ import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
import ca.uhn.fhir.rest.gclient.ICreateTyped;
import ca.uhn.fhir.rest.gclient.ICreateWithQuery;
import ca.uhn.fhir.rest.gclient.ICreateWithQueryTyped;
import ca.uhn.fhir.rest.gclient.ICriterion;
import ca.uhn.fhir.rest.gclient.ICriterionInternal;
import ca.uhn.fhir.rest.gclient.IDelete;
import ca.uhn.fhir.rest.gclient.IDeleteTyped;
import ca.uhn.fhir.rest.gclient.IDeleteWithQuery;
import ca.uhn.fhir.rest.gclient.IDeleteWithQueryTyped;
import ca.uhn.fhir.rest.gclient.IGetPage;
import ca.uhn.fhir.rest.gclient.IGetPageTyped;
import ca.uhn.fhir.rest.gclient.IGetTags;
@ -76,6 +80,8 @@ import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
import ca.uhn.fhir.rest.gclient.IUpdateExecutable;
import ca.uhn.fhir.rest.gclient.IUpdateTyped;
import ca.uhn.fhir.rest.gclient.IUpdateWithQuery;
import ca.uhn.fhir.rest.gclient.IUpdateWithQueryTyped;
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
@ -475,18 +481,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
return vread(theType, resId);
}
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
private EncodingEnum myParamEncoding;
private Boolean myPrettyPrint;
private boolean myQueryLogRequestAndResponse;
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
private static void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
private EncodingEnum myParamEncoding;
private Boolean myPrettyPrint;
private boolean myQueryLogRequestAndResponse;
@SuppressWarnings("unchecked")
@Override
public T andLogRequestAndResponse(boolean theLogRequestAndResponse) {
@ -575,11 +581,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped {
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
private String myId;
private IResource myResource;
private String myResourceBody;
private String mySearchUrl;
private CriterionList myCriterionList;
@Override
public MethodOutcome execute() {
@ -593,7 +601,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
myResourceBody = null;
}
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
BaseHttpClientInvocation invocation;
if (mySearchUrl != null) {
invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, mySearchUrl);
} else if (myCriterionList != null) {
invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext, myCriterionList.toParamList());
} else {
invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
@ -631,15 +646,50 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public ICreateTyped conditionalByUrl(String theSearchUrl) {
mySearchUrl = theSearchUrl;
return this;
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome> implements IDelete, IDeleteTyped {
@Override
public ICreateWithQuery conditional() {
myCriterionList = new CriterionList();
return this;
}
@Override
public ICreateWithQueryTyped where(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
@Override
public ICreateWithQueryTyped and(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome> implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped {
private IdDt myId;
private String mySearchUrl;
private String myResourceType;
private CriterionList myCriterionList;
@Override
public BaseOperationOutcome execute() {
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(myId);
HttpDeleteClientInvocation invocation;
if (myId != null) {
invocation = DeleteMethodBinding.createDeleteInvocation(myId);
} else if (myCriterionList != null) {
Map<String, List<String>> params = myCriterionList.toParamList();
invocation = DeleteMethodBinding.createDeleteInvocation(myResourceType, params);
} else {
invocation = DeleteMethodBinding.createDeleteInvocation(mySearchUrl);
}
OperationOutcomeResponseHandler binding = new OperationOutcomeResponseHandler();
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
@ -680,6 +730,56 @@ public class GenericClient extends BaseClient implements IGenericClient {
myId = new IdDt(theResourceType, theLogicalId);
return this;
}
@Override
public IDeleteTyped resourceConditionalByUrl(String theSearchUrl) {
Validate.notBlank(theSearchUrl, "theSearchUrl can not be blank/null");
mySearchUrl = theSearchUrl;
return this;
}
@Override
public IDeleteWithQuery resourceConditionalByType(String theResourceType) {
Validate.notBlank(theResourceType, "theResourceType can not be blank/null");
if (myContext.getResourceDefinition(theResourceType) == null) {
throw new IllegalArgumentException("Unknown resource type: " + theResourceType);
}
myResourceType = theResourceType;
myCriterionList = new CriterionList();
return this;
}
@Override
public IDeleteWithQueryTyped where(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
@Override
public IDeleteWithQueryTyped and(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
}
private static class CriterionList extends ArrayList<ICriterionInternal> {
private static final long serialVersionUID = 1L;
public void populateParamList(Map<String, List<String>> theParams) {
for (ICriterionInternal next : this) {
String parameterName = next.getParameterName();
String parameterValue = next.getParameterValue();
addParam(theParams, parameterName, parameterValue);
}
}
public Map<String, List<String>> toParamList() {
LinkedHashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
populateParamList(retVal);
return retVal;
}
}
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped, Bundle> implements IGetPageTyped {
@ -996,7 +1096,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private class SearchInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery, IUntypedQuery {
private String myCompartmentName;
private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>();
private CriterionList myCriterion = new CriterionList();
private List<Include> myInclude = new ArrayList<Include>();
private Integer myParamLimit;
private String myResourceId;
@ -1025,11 +1125,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
// params.putAll(initial);
// }
for (ICriterionInternal next : myCriterion) {
String parameterName = next.getParameterName();
String parameterValue = next.getParameterValue();
addParam(params, parameterName, parameterValue);
}
myCriterion.populateParamList(params);
for (Include next : myInclude) {
addParam(params, Constants.PARAM_INCLUDE, next.getValue());
@ -1224,30 +1320,39 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable {
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
private IdDt myId;
private IResource myResource;
private String myResourceBody;
private String mySearchUrl;
private CriterionList myCriterionList;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
// If an explicit encoding is chosen, we will re-serialize to ensure the right encoding
if (getParamEncoding() != null) {
myResourceBody = null;
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
BaseHttpClientInvocation invocation;
if (mySearchUrl != null) {
invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, mySearchUrl);
} else if (myCriterionList != null) {
invocation = MethodUtil.createUpdateInvocation(myContext, myResource, myResourceBody, myCriterionList.toParamList());
} else {
if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
@ -1297,6 +1402,30 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IUpdateTyped conditionalByUrl(String theSearchUrl) {
mySearchUrl = theSearchUrl;
return this;
}
@Override
public IUpdateWithQuery conditional() {
myCriterionList = new CriterionList();
return this;
}
@Override
public IUpdateWithQueryTyped where(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
@Override
public IUpdateWithQueryTyped and(ICriterion<?> theCriterion) {
myCriterionList.add((ICriterionInternal) theCriterion);
return this;
}
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.rest.gclient;
public interface IBaseQuery<T> {
T where(ICriterion<?> theCriterion);
T and(ICriterion<?> theCriterion);
}

View File

@ -37,4 +37,20 @@ public interface ICreateTyped extends IClientExecutable<ICreateTyped, MethodOutc
*/
ICreateTyped withId(IdDt theId);
/**
* Specifies that the create should be performed as a conditional create
* against a given search URL.
*
* @param theSearchUrl The search URL to use. The format of this URL should be of the form <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
*
* @since HAPI 0.9 / FHIR DSTU 2
*/
ICreateTyped conditionalByUrl(String theSearchUrl);
/**
* @since HAPI 0.9 / FHIR DSTU 2
*/
ICreateWithQuery conditional();
}

View File

@ -0,0 +1,6 @@
package ca.uhn.fhir.rest.gclient;
public interface ICreateWithQuery extends IBaseQuery<ICreateWithQueryTyped> {
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.gclient;
public interface ICreateWithQueryTyped extends ICreateTyped, ICreateWithQuery {
}

View File

@ -31,4 +31,20 @@ public interface IDelete {
IDeleteTyped resourceById(String theResourceType, String theLogicalId);
/**
* Specifies that the delete should be performed as a conditional delete
* against a given search URL.
*
* @param theSearchUrl The search URL to use. The format of this URL should be of the form <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
*
* @since HAPI 0.9 / FHIR DSTU 2
*/
IDeleteTyped resourceConditionalByUrl(String theSearchUrl);
/**
* @since HAPI 0.9 / FHIR DSTU 2
*/
IDeleteWithQuery resourceConditionalByType(String theResourceType);
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.gclient;
public interface IDeleteWithQuery extends IBaseQuery<IDeleteWithQueryTyped> {
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.gclient;
public interface IDeleteWithQueryTyped extends IDeleteTyped, IDeleteWithQuery {
}

View File

@ -24,11 +24,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
public interface IQuery extends IClientExecutable<IQuery,Bundle> {
IQuery where(ICriterion<?> theCriterion);
IQuery and(ICriterion<?> theCriterion);
public interface IQuery extends IClientExecutable<IQuery,Bundle>, IBaseQuery<IQuery> {
IQuery include(Include theIncludeManagingorganization);

View File

@ -28,4 +28,20 @@ public interface IUpdateTyped extends IUpdateExecutable {
IUpdateExecutable withId(String theId);
/**
* Specifies that the update should be performed as a conditional create
* against a given search URL.
*
* @param theSearchUrl The search URL to use. The format of this URL should be of the form <code>[ResourceType]?[Parameters]</code>,
* for example: <code>Patient?name=Smith&amp;identifier=13.2.4.11.4%7C847366</code>
*
* @since HAPI 0.9 / FHIR DSTU 2
*/
IUpdateTyped conditionalByUrl(String theSearchUrl);
/**
* @since HAPI 0.9 / FHIR DSTU 2
*/
IUpdateWithQuery conditional();
}

View File

@ -0,0 +1,6 @@
package ca.uhn.fhir.rest.gclient;
public interface IUpdateWithQuery extends IBaseQuery<IUpdateWithQueryTyped> {
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.gclient;
public interface IUpdateWithQueryTyped extends IUpdateTyped, IUpdateWithQuery {
}

View File

@ -143,29 +143,55 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
myResource = null;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
myResources = null;
myBundle = null;
myContents = theContents;
myContentsIsBundle = false;
myParams = theParams;
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
myResource = theResource;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
myResources = null;
myBundle = null;
myContents = null;
myContentsIsBundle = false;
myParams = theParams;
myBundleType = null;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
StringBuilder b = new StringBuilder();
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
b.append(theUrlBase);
url.append(theUrlBase);
} else {
if (!myUrlPath.contains("://")) {
b.append(theUrlBase);
url.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
url.append('/');
}
}
b.append(myUrlPath);
url.append(myUrlPath);
}
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
String url = b.toString();
appendExtraParamsWithQuestionMark(theExtraParams, url, url.indexOf("?") == -1);
if (myResource != null && BaseBinary.class.isAssignableFrom(myResource.getClass())) {
BaseBinary binary = (BaseBinary) myResource;
ByteArrayEntity entity = new ByteArrayEntity(binary.getContent(), ContentType.parse(binary.getContentType()));
HttpRequestBase retVal = createRequest(url, entity);
addMatchHeaders(retVal, url);
return retVal;
}
@ -228,6 +254,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
HttpRequestBase retVal = createRequest(url, entity);
super.addHeadersToRequest(retVal);
addMatchHeaders(retVal, url);
if (contentType != null) {
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
@ -236,6 +263,41 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
return retVal;
}
protected abstract HttpRequestBase createRequest(String url, AbstractHttpEntity theEntity);
private void addMatchHeaders(HttpRequestBase theHttpRequest, StringBuilder theUrlBase) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1);
theHttpRequest.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
if (myIfNoneExistString != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
b.append(b.indexOf("?") == -1 ? '?' : '&');
b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1));
theHttpRequest.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
}
private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') {
b.deleteCharAt(b.length() - 1);
}
return b;
}
protected abstract HttpRequestBase createRequest(StringBuilder theUrl, AbstractHttpEntity theEntity);
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
public void setIfNoneExistParams(Map<String, List<String>> theIfNoneExist) {
myIfNoneExistParams = theIfNoneExist;
}
public void setIfNoneExistString(String theIfNoneExistString) {
myIfNoneExistString = theIfNoneExistString;
}
}

View File

@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.method;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ca.uhn.fhir.context.ConfigurationException;
@ -154,5 +156,14 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return null;
}
public static HttpDeleteClientInvocation createDeleteInvocation(String theSearchUrl) {
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theSearchUrl);
return retVal;
}
public static HttpDeleteClientInvocation createDeleteInvocation(String theResourceType, Map<String, List<String>> theParams) {
return new HttpDeleteClientInvocation(theResourceType, theParams);
}
}

View File

@ -33,12 +33,22 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
private String myUrlPath;
private Map<String, List<String>> myParams;
public HttpDeleteClientInvocation(IdDt theId) {
super();
myUrlPath = theId.toUnqualifiedVersionless().getValue();
}
public HttpDeleteClientInvocation(String theSearchUrl) {
myUrlPath = theSearchUrl;
}
public HttpDeleteClientInvocation(String theResourceType, Map<String, List<String>> theParams) {
myUrlPath = theResourceType;
myParams = theParams;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) {
StringBuilder b = new StringBuilder();
@ -48,6 +58,7 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
}
b.append(myUrlPath);
appendExtraParamsWithQuestionMark(myParams, b, b.indexOf("?") == -1);
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
HttpDelete retVal = new HttpDelete(b.toString());
@ -55,5 +66,4 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
return retVal;
}
}

View File

@ -34,21 +34,19 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
public class HttpPostClientInvocation extends BaseHttpClientInvocationWithContents {
public HttpPostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
super(theContext, theResource, theUrlExtension);
}
public HttpPostClientInvocation(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
super(theContext, theTagList, theUrlExtension);
}
public HttpPostClientInvocation(FhirContext theContext, List<IResource> theResources, BundleTypeEnum theBundleType) {
super(theContext, theResources, theBundleType);
}
public HttpPostClientInvocation(FhirContext theContext, Bundle theBundle) {
super(theContext, theBundle);
}
@ -57,15 +55,13 @@ public class HttpPostClientInvocation extends BaseHttpClientInvocationWithConten
super(theContext, theContents, theIsBundle, theUrlExtension);
}
public HttpPostClientInvocation(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlExtension) {
super(theContext, theParams, theUrlExtension);
}
@Override
protected HttpPost createRequest(String url, AbstractHttpEntity theEntity) {
HttpPost retVal = new HttpPost(url);
protected HttpPost createRequest(StringBuilder theUrlBase, AbstractHttpEntity theEntity) {
HttpPost retVal = new HttpPost(theUrlBase.toString());
retVal.setEntity(theEntity);
return retVal;
}

View File

@ -38,11 +38,10 @@ public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContent
}
@Override
protected HttpRequestBase createRequest(String url, AbstractHttpEntity theEntity) {
HttpPut retVal = new HttpPut(url);
protected HttpRequestBase createRequest(StringBuilder theUrl, AbstractHttpEntity theEntity) {
HttpPut retVal = new HttpPut(theUrl.toString());
retVal.setEntity(theEntity);
return retVal;
}
}

View File

@ -5,14 +5,17 @@ import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@ -95,9 +98,65 @@ import ca.uhn.fhir.util.ReflectionUtil;
public class MethodUtil {
private static final String LABEL = "label=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
private static final String SCHEME = "scheme=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
static void addTagsToPostOrPut(IResource resource, BaseHttpClientInvocation retVal) {
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
retVal.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
}
}
}
}
public static HttpGetClientInvocation createConformanceInvocation() {
return new HttpGetClientInvocation("metadata");
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, FhirContext theContext) {
return createCreateInvocation(theResource, null, null, theContext);
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, String theResourceBody, String theId, FhirContext theContext) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
String resourceName = def.getName();
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
if (StringUtils.isNotBlank(theId)) {
urlExtension.append('/');
urlExtension.append(theId);
}
HttpPostClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
} else {
retVal = new HttpPostClientInvocation(theContext, theResourceBody, false, urlExtension.toString());
}
addTagsToPostOrPut(theResource, retVal);
// addContentTypeHeaderBasedOnDetectedType(retVal, theResourceBody);
return retVal;
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, String theResourceBody, String theId, FhirContext theContext, Map<String, List<String>> theIfNoneExistParams) {
HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theId, theContext);
retVal.setIfNoneExistParams(theIfNoneExistParams);
return retVal;
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, String theResourceBody, String theId, FhirContext theContext, String theIfNoneExistUrl) {
HttpPostClientInvocation retVal = createCreateInvocation(theResource, theResourceBody, theId, theContext);
retVal.setIfNoneExistString(theIfNoneExistUrl);
return retVal;
}
public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, String theResourceBody, IdDt theId, FhirContext theContext) {
String resourceName = theContext.getResourceDefinition(theResource).getName();
@ -105,9 +164,9 @@ public class MethodUtil {
urlBuilder.append(resourceName);
urlBuilder.append('/');
urlBuilder.append(theId.getIdPart());
String urlExtension = urlBuilder.toString();
HttpPutClientInvocation retVal;
String urlExtension = urlBuilder.toString();
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPutClientInvocation(theContext, theResource, urlExtension);
} else {
@ -135,203 +194,49 @@ public class MethodUtil {
return retVal;
}
public static void parseClientRequestResourceHeaders(IdDt theRequestedId, Map<String, List<String>> theHeaders, IBaseResource resource) {
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
String headerValue = lmHeaders.get(0);
Date headerDateValue;
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IResource theResource, String theResourceBody, Map<String, List<String>> theMatchParams) {
StringBuilder b = new StringBuilder();
String resourceType = theContext.getResourceDefinition(theResource).getName();
b.append(resourceType);
boolean haveQuestionMark=false;
for (Entry<String, List<String>> nextEntry : theMatchParams.entrySet()) {
for (String nextValue : nextEntry.getValue()) {
b.append(haveQuestionMark ? '&' : '?');
haveQuestionMark = true;
try {
headerDateValue = DateUtils.parseDate(headerValue);
if (resource instanceof IResource) {
InstantDt lmValue = new InstantDt(headerDateValue);
((IResource) resource).getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
} else if (resource instanceof IAnyResource) {
((IAnyResource) resource).getMeta().setLastUpdated(headerDateValue);
}
} catch (Exception e) {
ourLog.warn("Unable to parse date string '{}'. Error is: {}", headerValue, e.toString());
}
}
List<String> clHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
if (clHeaders != null && clHeaders.size() > 0 && StringUtils.isNotBlank(clHeaders.get(0))) {
String headerValue = clHeaders.get(0);
if (isNotBlank(headerValue)) {
new IdDt(headerValue).applyTo(resource);
}
}
IdDt existing = IdDt.of(resource);
List<String> eTagHeaders = theHeaders.get(Constants.HEADER_ETAG_LC);
String eTagVersion = null;
if (eTagHeaders != null && eTagHeaders.size() > 0) {
eTagVersion = parseETagValue(eTagHeaders.get(0));
}
if (isNotBlank(eTagVersion)) {
if (existing == null || existing.isEmpty()) {
if (theRequestedId != null) {
theRequestedId.withVersion(eTagVersion).applyTo(resource);
}
} else if (existing.hasVersionIdPart() == false) {
existing.withVersion(eTagVersion).applyTo(resource);
}
} else if (existing == null || existing.isEmpty()) {
if (theRequestedId != null) {
theRequestedId.applyTo(resource);
}
}
List<String> categoryHeaders = theHeaders.get(Constants.HEADER_CATEGORY_LC);
if (categoryHeaders != null && categoryHeaders.size() > 0 && StringUtils.isNotBlank(categoryHeaders.get(0))) {
TagList tagList = new TagList();
for (String header : categoryHeaders) {
parseTagValue(tagList, header);
}
if (resource instanceof IResource) {
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) resource, tagList);
} else if (resource instanceof IAnyResource) {
IMetaType meta = ((IAnyResource) resource).getMeta();
for (Tag next : tagList) {
meta.addTag().setSystem(next.getScheme()).setCode(next.getTerm()).setDisplay(next.getLabel());
}
b.append(URLEncoder.encode(nextEntry.getKey(), "UTF-8"));
b.append('=');
b.append(URLEncoder.encode(nextValue, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new ConfigurationException("UTF-8 not supported on this platform");
}
}
}
public static String parseETagValue(String value) {
String eTagVersion;
value = value.trim();
if (value.length() > 1) {
if (value.charAt(value.length() - 1) == '"') {
if (value.charAt(0) == '"') {
eTagVersion = value.substring(1, value.length() - 1);
} else if (value.length() > 3 && value.charAt(0) == 'W' && value.charAt(1) == '/' && value.charAt(2) == '"') {
eTagVersion = value.substring(3, value.length() - 1);
} else {
eTagVersion = value;
}
} else {
eTagVersion = value;
}
} else {
eTagVersion = value;
}
return eTagVersion;
}
public static void parseTagValue(TagList tagList, String nextTagComplete) {
StringBuilder next = new StringBuilder(nextTagComplete);
parseTagValue(tagList, nextTagComplete, next);
}
private static void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) {
int firstSemicolon = theBuffer.indexOf(";");
int deleteTo;
if (firstSemicolon == -1) {
firstSemicolon = theBuffer.indexOf(",");
if (firstSemicolon == -1) {
firstSemicolon = theBuffer.length();
deleteTo = theBuffer.length();
} else {
deleteTo = firstSemicolon;
}
} else {
deleteTo = firstSemicolon + 1;
}
String term = theBuffer.substring(0, firstSemicolon);
String scheme = null;
String label = null;
if (isBlank(term)) {
return;
}
theBuffer.delete(0, deleteTo);
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
theBuffer.deleteCharAt(0);
}
while (theBuffer.length() > 0) {
boolean foundSomething = false;
if (theBuffer.length() > SCHEME.length() && theBuffer.substring(0, SCHEME.length()).equals(SCHEME)) {
int closeIdx = theBuffer.indexOf("\"", SCHEME.length());
scheme = theBuffer.substring(SCHEME.length(), closeIdx);
theBuffer.delete(0, closeIdx + 1);
foundSomething = true;
}
if (theBuffer.length() > LABEL.length() && theBuffer.substring(0, LABEL.length()).equals(LABEL)) {
int closeIdx = theBuffer.indexOf("\"", LABEL.length());
label = theBuffer.substring(LABEL.length(), closeIdx);
theBuffer.delete(0, closeIdx + 1);
foundSomething = true;
}
// TODO: support enc2231-string as described in
// http://tools.ietf.org/html/draft-johnston-http-category-header-02
// TODO: support multiple tags in one header as described in
// http://hl7.org/implement/standards/fhir/http.html#tags
while (theBuffer.length() > 0 && (theBuffer.charAt(0) == ' ' || theBuffer.charAt(0) == ';')) {
theBuffer.deleteCharAt(0);
}
if (!foundSomething) {
break;
}
}
if (theBuffer.length() > 0 && theBuffer.charAt(0) == ',') {
theBuffer.deleteCharAt(0);
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
theBuffer.deleteCharAt(0);
}
theTagList.add(new Tag(scheme, term, label));
parseTagValue(theTagList, theCompleteHeaderValue, theBuffer);
} else {
theTagList.add(new Tag(scheme, term, label));
}
if (theBuffer.length() > 0) {
ourLog.warn("Ignoring extra text at the end of " + Constants.HEADER_CATEGORY + " tag '" + theBuffer.toString() + "' - Complete tag value was: " + theCompleteHeaderValue);
}
}
static void addTagsToPostOrPut(IResource resource, BaseHttpClientInvocation retVal) {
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
retVal.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
}
}
}
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, FhirContext theContext) {
return createCreateInvocation(theResource, null, null, theContext);
}
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, String theResourceBody, String theId, FhirContext theContext) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
String resourceName = def.getName();
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
if (StringUtils.isNotBlank(theId)) {
urlExtension.append('/');
urlExtension.append(theId);
}
HttpPostClientInvocation retVal;
HttpPutClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
retVal = new HttpPutClientInvocation(theContext, theResource, b.toString());
} else {
retVal = new HttpPostClientInvocation(theContext, theResourceBody, false, urlExtension.toString());
retVal = new HttpPutClientInvocation(theContext, theResourceBody, false, b.toString());
}
addTagsToPostOrPut(theResource, retVal);
// addContentTypeHeaderBasedOnDetectedType(retVal, theResourceBody);
return retVal;
}
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IResource theResource, String theResourceBody, String theMatchUrl) {
HttpPutClientInvocation retVal;
if (StringUtils.isBlank(theResourceBody)) {
retVal = new HttpPutClientInvocation(theContext, theResource, theMatchUrl);
} else {
retVal = new HttpPutClientInvocation(theContext, theResourceBody, false,theMatchUrl);
}
addTagsToPostOrPut(theResource, retVal);
return retVal;
}
@ -348,57 +253,21 @@ public class MethodUtil {
return EncodingEnum.XML;
}
public static HttpGetClientInvocation createConformanceInvocation() {
return new HttpGetClientInvocation("metadata");
}
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
List<String> locationHeaders = new ArrayList<String>();
List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
if (lh != null) {
locationHeaders.addAll(lh);
}
List<String> clh = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
if (clh != null) {
locationHeaders.addAll(clh);
}
MethodOutcome retVal = new MethodOutcome();
if (locationHeaders != null && locationHeaders.size() > 0) {
String locationHeader = locationHeaders.get(0);
BaseOutcomeReturningMethodBinding.parseContentLocation(retVal, theResourceName, locationHeader);
}
if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType);
if (ct != null) {
PushbackReader reader = new PushbackReader(theResponseReader);
try {
int firstByte = reader.read();
if (firstByte == -1) {
BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read");
reader = null;
public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
for (Annotation annotation : theAnnotations) {
if (annotation instanceof Description) {
Description desc = (Description) annotation;
if (isNotBlank(desc.formalDefinition())) {
theParameter.setDescription(desc.formalDefinition());
} else {
reader.unread(firstByte);
theParameter.setDescription(desc.shortDefinition());
}
} catch (IOException e) {
BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read", e);
reader = null;
}
if (reader != null) {
IParser parser = ct.newParser(theContext);
IResource outcome = parser.parseResource(reader);
if (outcome instanceof BaseOperationOutcome) {
retVal.setOperationOutcome((BaseOperationOutcome) outcome);
}
}
} else {
BaseOutcomeReturningMethodBinding.ourLog.debug("Ignoring response content of type: {}", theResponseMimeType);
}
}
return retVal;
public static Integer findIdParameterIndex(Method theMethod) {
return MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class);
}
public static Integer findParamAnnotationIndex(Method theMethod, Class<?> toFind) {
@ -416,38 +285,8 @@ public class MethodUtil {
return null;
}
public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
for (Annotation annotation : theAnnotations) {
if (annotation instanceof Description) {
Description desc = (Description) annotation;
if (isNotBlank(desc.formalDefinition())) {
theParameter.setDescription(desc.formalDefinition());
} else {
theParameter.setDescription(desc.shortDefinition());
}
}
}
}
public static IQueryParameterOr<?> singleton(final IQueryParameterType theParam) {
return new IQueryParameterOr<IQueryParameterType>() {
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
if (theParameters.isEmpty()) {
return;
}
if (theParameters.size() > 1) {
throw new IllegalArgumentException("Type " + theParam.getClass().getCanonicalName() + " does not support multiple values");
}
theParam.setValueAsQueryToken(theParameters.getQualifier(), theParameters.get(0));
}
@Override
public List<IQueryParameterType> getValuesAsQueryTokens() {
return Collections.singletonList(theParam);
}
};
public static Integer findTagListParameterIndex(Method theMethod) {
return MethodUtil.findParamAnnotationIndex(theMethod, TagListParam.class);
}
@SuppressWarnings("deprecation")
@ -455,46 +294,6 @@ public class MethodUtil {
return MethodUtil.findParamAnnotationIndex(theMethod, VersionIdParam.class);
}
public static Integer findIdParameterIndex(Method theMethod) {
return MethodUtil.findParamAnnotationIndex(theMethod, IdParam.class);
}
public static Integer findTagListParameterIndex(Method theMethod) {
return MethodUtil.findParamAnnotationIndex(theMethod, TagListParam.class);
}
/**
* This is a utility method intended provided to help the JPA module.
*/
public static IQueryParameterAnd<?> parseQueryParams(RuntimeSearchParam theParamDef, String theUnqualifiedParamName, List<QualifiedParamList> theParameters) {
QueryParameterAndBinder binder = null;
switch (theParamDef.getParamType()) {
case COMPOSITE:
throw new UnsupportedOperationException();
case DATE:
binder = new QueryParameterAndBinder(DateAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case NUMBER:
binder = new QueryParameterAndBinder(NumberAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case QUANTITY:
binder = new QueryParameterAndBinder(QuantityAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case REFERENCE:
binder = new QueryParameterAndBinder(ReferenceAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case STRING:
binder = new QueryParameterAndBinder(StringAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case TOKEN:
binder = new QueryParameterAndBinder(TokenAndListParam.class, Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
}
return binder.parse(theUnqualifiedParamName, theParameters);
}
@SuppressWarnings("unchecked")
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider) {
List<IParameter> parameters = new ArrayList<IParameter>();
@ -612,4 +411,267 @@ public class MethodUtil {
return parameters;
}
public static void parseClientRequestResourceHeaders(IdDt theRequestedId, Map<String, List<String>> theHeaders, IBaseResource resource) {
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
String headerValue = lmHeaders.get(0);
Date headerDateValue;
try {
headerDateValue = DateUtils.parseDate(headerValue);
if (resource instanceof IResource) {
InstantDt lmValue = new InstantDt(headerDateValue);
((IResource) resource).getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
} else if (resource instanceof IAnyResource) {
((IAnyResource) resource).getMeta().setLastUpdated(headerDateValue);
}
} catch (Exception e) {
ourLog.warn("Unable to parse date string '{}'. Error is: {}", headerValue, e.toString());
}
}
List<String> clHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
if (clHeaders != null && clHeaders.size() > 0 && StringUtils.isNotBlank(clHeaders.get(0))) {
String headerValue = clHeaders.get(0);
if (isNotBlank(headerValue)) {
new IdDt(headerValue).applyTo(resource);
}
}
IdDt existing = IdDt.of(resource);
List<String> eTagHeaders = theHeaders.get(Constants.HEADER_ETAG_LC);
String eTagVersion = null;
if (eTagHeaders != null && eTagHeaders.size() > 0) {
eTagVersion = parseETagValue(eTagHeaders.get(0));
}
if (isNotBlank(eTagVersion)) {
if (existing == null || existing.isEmpty()) {
if (theRequestedId != null) {
theRequestedId.withVersion(eTagVersion).applyTo(resource);
}
} else if (existing.hasVersionIdPart() == false) {
existing.withVersion(eTagVersion).applyTo(resource);
}
} else if (existing == null || existing.isEmpty()) {
if (theRequestedId != null) {
theRequestedId.applyTo(resource);
}
}
List<String> categoryHeaders = theHeaders.get(Constants.HEADER_CATEGORY_LC);
if (categoryHeaders != null && categoryHeaders.size() > 0 && StringUtils.isNotBlank(categoryHeaders.get(0))) {
TagList tagList = new TagList();
for (String header : categoryHeaders) {
parseTagValue(tagList, header);
}
if (resource instanceof IResource) {
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) resource, tagList);
} else if (resource instanceof IAnyResource) {
IMetaType meta = ((IAnyResource) resource).getMeta();
for (Tag next : tagList) {
meta.addTag().setSystem(next.getScheme()).setCode(next.getTerm()).setDisplay(next.getLabel());
}
}
}
}
public static String parseETagValue(String value) {
String eTagVersion;
value = value.trim();
if (value.length() > 1) {
if (value.charAt(value.length() - 1) == '"') {
if (value.charAt(0) == '"') {
eTagVersion = value.substring(1, value.length() - 1);
} else if (value.length() > 3 && value.charAt(0) == 'W' && value.charAt(1) == '/' && value.charAt(2) == '"') {
eTagVersion = value.substring(3, value.length() - 1);
} else {
eTagVersion = value;
}
} else {
eTagVersion = value;
}
} else {
eTagVersion = value;
}
return eTagVersion;
}
/**
* This is a utility method intended provided to help the JPA module.
*/
public static IQueryParameterAnd<?> parseQueryParams(RuntimeSearchParam theParamDef, String theUnqualifiedParamName, List<QualifiedParamList> theParameters) {
QueryParameterAndBinder binder = null;
switch (theParamDef.getParamType()) {
case COMPOSITE:
throw new UnsupportedOperationException();
case DATE:
binder = new QueryParameterAndBinder(DateAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case NUMBER:
binder = new QueryParameterAndBinder(NumberAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case QUANTITY:
binder = new QueryParameterAndBinder(QuantityAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case REFERENCE:
binder = new QueryParameterAndBinder(ReferenceAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case STRING:
binder = new QueryParameterAndBinder(StringAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case TOKEN:
binder = new QueryParameterAndBinder(TokenAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
}
return binder.parse(theUnqualifiedParamName, theParameters);
}
public static void parseTagValue(TagList tagList, String nextTagComplete) {
StringBuilder next = new StringBuilder(nextTagComplete);
parseTagValue(tagList, nextTagComplete, next);
}
private static void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) {
int firstSemicolon = theBuffer.indexOf(";");
int deleteTo;
if (firstSemicolon == -1) {
firstSemicolon = theBuffer.indexOf(",");
if (firstSemicolon == -1) {
firstSemicolon = theBuffer.length();
deleteTo = theBuffer.length();
} else {
deleteTo = firstSemicolon;
}
} else {
deleteTo = firstSemicolon + 1;
}
String term = theBuffer.substring(0, firstSemicolon);
String scheme = null;
String label = null;
if (isBlank(term)) {
return;
}
theBuffer.delete(0, deleteTo);
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
theBuffer.deleteCharAt(0);
}
while (theBuffer.length() > 0) {
boolean foundSomething = false;
if (theBuffer.length() > SCHEME.length() && theBuffer.substring(0, SCHEME.length()).equals(SCHEME)) {
int closeIdx = theBuffer.indexOf("\"", SCHEME.length());
scheme = theBuffer.substring(SCHEME.length(), closeIdx);
theBuffer.delete(0, closeIdx + 1);
foundSomething = true;
}
if (theBuffer.length() > LABEL.length() && theBuffer.substring(0, LABEL.length()).equals(LABEL)) {
int closeIdx = theBuffer.indexOf("\"", LABEL.length());
label = theBuffer.substring(LABEL.length(), closeIdx);
theBuffer.delete(0, closeIdx + 1);
foundSomething = true;
}
// TODO: support enc2231-string as described in
// http://tools.ietf.org/html/draft-johnston-http-category-header-02
// TODO: support multiple tags in one header as described in
// http://hl7.org/implement/standards/fhir/http.html#tags
while (theBuffer.length() > 0 && (theBuffer.charAt(0) == ' ' || theBuffer.charAt(0) == ';')) {
theBuffer.deleteCharAt(0);
}
if (!foundSomething) {
break;
}
}
if (theBuffer.length() > 0 && theBuffer.charAt(0) == ',') {
theBuffer.deleteCharAt(0);
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
theBuffer.deleteCharAt(0);
}
theTagList.add(new Tag(scheme, term, label));
parseTagValue(theTagList, theCompleteHeaderValue, theBuffer);
} else {
theTagList.add(new Tag(scheme, term, label));
}
if (theBuffer.length() > 0) {
ourLog.warn("Ignoring extra text at the end of " + Constants.HEADER_CATEGORY + " tag '" + theBuffer.toString() + "' - Complete tag value was: " + theCompleteHeaderValue);
}
}
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
List<String> locationHeaders = new ArrayList<String>();
List<String> lh = theHeaders.get(Constants.HEADER_LOCATION_LC);
if (lh != null) {
locationHeaders.addAll(lh);
}
List<String> clh = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
if (clh != null) {
locationHeaders.addAll(clh);
}
MethodOutcome retVal = new MethodOutcome();
if (locationHeaders != null && locationHeaders.size() > 0) {
String locationHeader = locationHeaders.get(0);
BaseOutcomeReturningMethodBinding.parseContentLocation(retVal, theResourceName, locationHeader);
}
if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType);
if (ct != null) {
PushbackReader reader = new PushbackReader(theResponseReader);
try {
int firstByte = reader.read();
if (firstByte == -1) {
BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read");
reader = null;
} else {
reader.unread(firstByte);
}
} catch (IOException e) {
BaseOutcomeReturningMethodBinding.ourLog.debug("No content in response, not going to read", e);
reader = null;
}
if (reader != null) {
IParser parser = ct.newParser(theContext);
IResource outcome = parser.parseResource(reader);
if (outcome instanceof BaseOperationOutcome) {
retVal.setOperationOutcome((BaseOperationOutcome) outcome);
}
}
} else {
BaseOutcomeReturningMethodBinding.ourLog.debug("Ignoring response content of type: {}", theResponseMimeType);
}
}
return retVal;
}
public static IQueryParameterOr<?> singleton(final IQueryParameterType theParam) {
return new IQueryParameterOr<IQueryParameterType>() {
@Override
public List<IQueryParameterType> getValuesAsQueryTokens() {
return Collections.singletonList(theParam);
}
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
if (theParameters.isEmpty()) {
return;
}
if (theParameters.size() > 1) {
throw new IllegalArgumentException("Type " + theParam.getClass().getCanonicalName() + " does not support multiple values");
}
theParam.setValueAsQueryToken(theParameters.getQualifier(), theParameters.get(0));
}
};
}
}

View File

@ -111,6 +111,8 @@ public class Constants {
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
public static final String HEADER_IF_NONE_EXIST = "If-None-Exist";
public static final String HEADER_IF_NONE_EXIST_LC = HEADER_IF_NONE_EXIST.toLowerCase();
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -24,7 +24,16 @@ ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not f
# JPA Messages
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request.
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionOperationWithMultipleMatchFailure=Failed to {0} resource with match URL "{1}" because this search matched {2} resources
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionOperationFailedNoId=Failed to {0} resource in transaction because no ID was provided
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionOperationFailedUnknownId=Failed to {0} resource in transaction because no resource could be found with ID {1}
ca.uhn.fhir.jpa.dao.BaseFhirDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request.
ca.uhn.fhir.jpa.dao.BaseFhirDao.transactionOperationWithMultipleMatchFailure=Failed to {0} resource with match URL "{1}" because this search matched {2} resources
ca.uhn.fhir.jpa.dao.BaseFhirDao.transactionOperationFailedNoId=Failed to {0} resource in transaction because no ID was provided
ca.uhn.fhir.jpa.dao.BaseFhirDao.transactionOperationFailedUnknownId=Failed to {0} resource in transaction because no resource could be found with ID {1}
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionContainsMultipleWithDuplicateId=Transaction bundle contains multiple resources with ID: {0}
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry.transaction.method. Found value: "{0}"
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided.
ca.uhn.fhir.jpa.dao.BaseFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
ca.uhn.fhir.jpa.dao.FhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.FhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create entity with ID[{0}], this server does not allow clients to assign numeric IDs
ca.uhn.fhir.jpa.dao.FhirResourceDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed.

View File

@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@ -97,4 +97,4 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.compiler.source=1.6

View File

@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collection;
@ -48,6 +50,8 @@ import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.instance.model.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
@ -77,6 +81,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.ResourceTag;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
@ -87,6 +92,8 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -95,15 +102,16 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.FhirTerser;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
public abstract class BaseFhirDao implements IDao {
public static final String UCUM_NS = "http://unitsofmeasure.org";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseFhirDao.class);
private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>();
public static final String UCUM_NS = "http://unitsofmeasure.org";
@Autowired(required = true)
private DaoConfig myConfig;
@ -114,8 +122,6 @@ public abstract class BaseFhirDao implements IDao {
private EntityManager myEntityManager;
private List<IDaoListener> myListeners = new ArrayList<IDaoListener>();
private ISearchParamExtractor mySearchParamExtractor;
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
@ -124,6 +130,8 @@ public abstract class BaseFhirDao implements IDao {
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
private ISearchParamExtractor mySearchParamExtractor;
protected void createForcedIdIfNeeded(ResourceTable entity, IdDt id) {
if (id.isEmpty() == false && id.hasIdPart()) {
if (isValidPid(id)) {
@ -508,19 +516,6 @@ public abstract class BaseFhirDao implements IDao {
return retVal;
}
protected static String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
return new String(out).toUpperCase();
}
protected void notifyWriteCompleted() {
for (IDaoListener next : myListeners) {
next.writeCompleted();
@ -579,6 +574,58 @@ public abstract class BaseFhirDao implements IDao {
}
protected Set<Long> processMatchUrl(String theMatchUrl, Class<? extends IBaseResource> theResourceType) {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
IFhirResourceDao<? extends IResource> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
return ids;
}
protected SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
SearchParameterMap paramMap = new SearchParameterMap();
List<NameValuePair> parameters;
try {
parameters = URLEncodedUtils.parse(new URI(theMatchUrl), "UTF-8");
} catch (URISyntaxException e) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
}
ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create();
for (NameValuePair next : parameters) {
String paramName = next.getName();
String qualifier = null;
for (int i = 0; i < paramMap.size(); i++) {
switch (paramName.charAt(i)) {
case '.':
case ':':
qualifier = paramName.substring(i);
paramName = paramName.substring(0, i);
i = Integer.MAX_VALUE;
break;
}
}
QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, next.getValue());
nameToParamLists.put(paramName, paramList);
}
for (String nextParamName : nameToParamLists.keySet()) {
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
if (paramDef == null) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
}
List<QualifiedParamList> paramList = nameToParamLists.get(nextParamName);
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList);
paramMap.add(nextParamName, param);
}
return paramMap;
}
@Override
public void registerDaoListener(IDaoListener theListener) {
Validate.notNull(theListener, "theListener");
@ -832,7 +879,20 @@ public abstract class BaseFhirDao implements IDao {
}
}
protected String translatePidIdToForcedId(Long theId) {
ForcedId forcedId = myEntityManager.find(ForcedId.class, theId);
if (forcedId != null) {
return forcedId.getForcedId();
} else {
return theId.toString();
}
}
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull) {
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true,true);
}
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion) {
if (entity.getPublished() == null) {
entity.setPublished(new Date());
}
@ -849,7 +909,9 @@ public abstract class BaseFhirDao implements IDao {
myEntityManager.persist(historyEntry);
}
if (theUpdateVersion) {
entity.setVersion(entity.getVersion() + 1);
}
Collection<ResourceIndexedSearchParamString> paramsString = new ArrayList<ResourceIndexedSearchParamString>(entity.getParamsString());
Collection<ResourceIndexedSearchParamToken> paramsToken = new ArrayList<ResourceIndexedSearchParamToken>(entity.getParamsToken());
@ -858,12 +920,13 @@ public abstract class BaseFhirDao implements IDao {
Collection<ResourceIndexedSearchParamDate> paramsDate = new ArrayList<ResourceIndexedSearchParamDate>(entity.getParamsDate());
Collection<ResourceLink> resourceLinks = new ArrayList<ResourceLink>(entity.getResourceLinks());
final List<ResourceIndexedSearchParamString> stringParams;
final List<ResourceIndexedSearchParamToken> tokenParams;
final List<ResourceIndexedSearchParamNumber> numberParams;
final List<ResourceIndexedSearchParamQuantity> quantityParams;
final List<ResourceIndexedSearchParamDate> dateParams;
final List<ResourceLink> links;
List<ResourceIndexedSearchParamString> stringParams = null;
List<ResourceIndexedSearchParamToken> tokenParams = null;
List<ResourceIndexedSearchParamNumber> numberParams = null;
List<ResourceIndexedSearchParamQuantity> quantityParams = null;
List<ResourceIndexedSearchParamDate> dateParams = null;
List<ResourceLink> links = null;
if (theDeletedTimestampOrNull != null) {
stringParams = Collections.emptyList();
@ -875,7 +938,7 @@ public abstract class BaseFhirDao implements IDao {
entity.setDeleted(theDeletedTimestampOrNull);
entity.setUpdated(theDeletedTimestampOrNull);
} else {
} else if (thePerformIndexing) {
stringParams = extractSearchParamStrings(entity, theResource);
numberParams = extractSearchParamNumber(entity, theResource);
@ -893,7 +956,6 @@ public abstract class BaseFhirDao implements IDao {
links = extractResourceLinks(entity, theResource);
populateResourceIntoEntity(theResource, entity);
entity.setUpdated(new Date());
entity.setLanguage(theResource.getLanguage().getValue());
entity.setParamsString(stringParams);
@ -909,6 +971,12 @@ public abstract class BaseFhirDao implements IDao {
entity.setResourceLinks(links);
entity.setHasLinks(links.isEmpty() == false);
} else {
populateResourceIntoEntity(theResource, entity);
entity.setUpdated(new Date());
entity.setLanguage(theResource.getLanguage().getValue());
}
if (entity.getId() == null) {
@ -922,6 +990,8 @@ public abstract class BaseFhirDao implements IDao {
entity = myEntityManager.merge(entity);
}
if (thePerformIndexing) {
if (entity.isParamsStringPopulated()) {
for (ResourceIndexedSearchParamString next : paramsString) {
myEntityManager.remove(next);
@ -976,6 +1046,8 @@ public abstract class BaseFhirDao implements IDao {
myEntityManager.persist(next);
}
} // if thePerformIndexing
myEntityManager.flush();
if (theResource != null) {
@ -985,4 +1057,17 @@ public abstract class BaseFhirDao implements IDao {
return entity;
}
protected static String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
return new String(out).toUpperCase();
}
}

View File

@ -20,11 +20,8 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -36,27 +33,14 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.google.common.collect.ArrayListMultimap;
public abstract class BaseFhirSystemDao<T> extends BaseFhirDao implements IFhirSystemDao<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseFhirSystemDao.class);
@ -82,53 +66,6 @@ public abstract class BaseFhirSystemDao<T> extends BaseFhirDao implements IFhirS
return myEntityManager.find(ResourceTable.class, candidateMatches.iterator().next());
}
protected Set<Long> processMatchUrl(String theMatchUrl, Class<? extends IBaseResource> theResourceType) {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
SearchParameterMap paramMap = new SearchParameterMap();
List<NameValuePair> parameters;
try {
parameters = URLEncodedUtils.parse(new URI(theMatchUrl), "UTF-8");
} catch (URISyntaxException e) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
}
ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create();
for (NameValuePair next : parameters) {
String paramName = next.getName();
String qualifier = null;
for (int i = 0; i < paramMap.size(); i++) {
switch (paramName.charAt(i)) {
case '.':
case ':':
qualifier = paramName.substring(i);
paramName = paramName.substring(0, i);
i = Integer.MAX_VALUE;
break;
}
}
QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, next.getValue());
nameToParamLists.put(paramName, paramList);
}
for (String nextParamName : nameToParamLists.keySet()) {
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
if (paramDef == null) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
}
List<QualifiedParamList> paramList = nameToParamLists.get(nextParamName);
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList);
paramMap.add(nextParamName, param);
}
IFhirResourceDao<? extends IResource> dao = getDao(theResourceType);
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
return ids;
}
@Override
public IBundleProvider history(Date theSince) {
StopWatch w = new StopWatch();

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.MethodOutcome;
public class DaoMethodOutcome extends MethodOutcome {
private ResourceTable myEntity;
private IResource myResource;
public ResourceTable getEntity() {
return myEntity;
}
public IResource getResource() {
return myResource;
}
@Override
public DaoMethodOutcome setCreated(Boolean theCreated) {
super.setCreated(theCreated);
return this;
}
public DaoMethodOutcome setEntity(ResourceTable theEntity) {
myEntity = theEntity;
return this;
}
public DaoMethodOutcome setResource(IResource theResource) {
myResource = theResource;
return this;
}
}

View File

@ -20,41 +20,237 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryTransactionResponse;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.FhirTerser;
public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class);
private UrlParts parseUrl(String theAction, String theUrl) {
UrlParts retVal = new UrlParts();
//@formatter:off
/*
* We assume that the URL passed in is in one of the following forms:
* [Resource Type]?[Search Params]
* [Resource Type]/[Resource ID]
* [Resource Type]/[Resource ID]/_history/[Version ID]
*/
//@formatter:on
int nextStart = 0;
boolean nextIsHistory = false;
for (int idx = 0; idx < theUrl.length(); idx++) {
char nextChar = theUrl.charAt(idx);
boolean atEnd = (idx + 1) == theUrl.length();
if (nextChar == '?' || nextChar == '/' || atEnd) {
int endIdx = atEnd ? idx + 1 : idx;
String nextSubstring = theUrl.substring(nextStart, endIdx);
if (retVal.getResourceType() == null) {
retVal.setResourceType(nextSubstring);
} else if (retVal.getResourceId() == null) {
retVal.setResourceId(nextSubstring);
} else if (nextIsHistory) {
retVal.setVersionId(nextSubstring);
} else {
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
nextIsHistory = true;
} else {
String msg = getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
}
if (nextChar == '?') {
if (theUrl.length() > idx + 1) {
retVal.setParams(theUrl.substring(idx + 1, theUrl.length()));
}
break;
}
nextStart = idx + 1;
}
}
RuntimeResourceDefinition resType = getContext().getResourceDefinition(retVal.getResourceType());
IFhirResourceDao<? extends IResource> dao = null;
if (resType != null) {
dao = getDao(resType.getImplementingClass());
}
if (dao == null) {
String msg = getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
retVal.setDao(dao);
if (retVal.getResourceId() == null && retVal.getParams() == null) {
String msg = getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
return retVal;
}
@SuppressWarnings("unchecked")
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Bundle transaction(Bundle theResources) {
ourLog.info("Beginning transaction with {} resources", theResources.getEntry().size());
long start = System.currentTimeMillis();
// Set<IdDt> allIds = new HashSet<IdDt>();
//
// for (int i = 0; i < theResources.size(); i++) {
// IResource res = theResources.get(i);
// if (res.getId().hasIdPart() && !res.getId().hasResourceType()) {
// res.setId(new IdDt(toResourceName(res.getClass()), res.getId().getIdPart()));
// }
//
// /*
// * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
// */
// if (res.getId().hasResourceType() && res.getId().hasIdPart()) {
// IdDt nextId = res.getId().toUnqualifiedVersionless();
// if (!allIds.add(nextId)) {
// throw new InvalidRequestException("Transaction bundle contains multiple resources with ID: " + nextId);
// }
// }
// }
//
// FhirTerser terser = getContext().newTerser();
//
Set<IdDt> allIds = new HashSet<IdDt>();
Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>();
Map<IdDt, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdDt, DaoMethodOutcome>();
Bundle response = new Bundle();
OperationOutcome oo = new OperationOutcome();
response.addEntry().setResource(oo);
for (int i = 0; i < theResources.getEntry().size(); i++) {
Entry nextEntry = theResources.getEntry().get(i);
IResource res = nextEntry.getResource();
IdDt nextResourceId = null;
if (res != null) {
nextResourceId = res.getId();
if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType()) {
nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
res.setId(nextResourceId);
}
/*
* Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
*/
if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
IdDt nextId = nextResourceId.toUnqualifiedVersionless();
if (!allIds.add(nextId)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
}
}
}
HTTPVerbEnum verb = nextEntry.getTransaction().getMethodElement().getValueAsEnum();
if (verb == null) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextEntry.getTransaction().getMethod()));
}
switch (verb) {
case POST: {
// CREATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
res.setId(null);
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
outcome = resourceDao.create(res, nextEntry.getTransaction().getIfNoneExist(), false);
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
}
case DELETE: {
// DELETE
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (parts.getResourceId() != null) {
parts.getDao().delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
} else {
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
}
newEntry.getTransactionResponse().setStatus(Integer.toString(Constants.STATUS_HTTP_204_NO_CONTENT));
break;
}
case PUT: {
// UPDATE
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = getDao(res.getClass());
DaoMethodOutcome outcome;
Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
if (parts.getResourceId() != null) {
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
outcome = resourceDao.update(res, null, false);
} else {
res.setId(null);
outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false);
}
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, newEntry);
break;
}
case GET: {
// SEARCH/READ/VREAD
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url);
@SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = parts.getDao();
if (parts.getResourceId() != null && parts.getParams() == null) {
IResource found;
if (parts.getVersionId() != null) {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else {
found = resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
}
EntryTransactionResponse resp = response.addEntry().setResource(found).getTransactionResponse();
resp.setLocation(found.getId().toUnqualified().getValue());
resp.addEtag(found.getId().getVersionIdPart());
} else if (parts.getParams() != null) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType());
SearchParameterMap params = translateMatchUrl(url, def);
IBundleProvider bundle = parts.getDao().search(params);
Bundle searchBundle = new Bundle();
searchBundle.setTotal(bundle.size());
int configuredMax = 100; // this should probably be configurable or something
if (bundle.size() > configuredMax) {
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
}
List<IResource> resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
for (IResource next : resourcesToAdd) {
searchBundle.addEntry().setResource(next);
}
response.addEntry().setResource(searchBundle);
}
}
}
}
FhirTerser terser = getContext().newTerser();
// int creations = 0;
// int updates = 0;
//
@ -75,7 +271,8 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
// }
//
// String resourceName = toResourceName(nextResource);
// BundleEntryTransactionOperationEnum nextResouceOperationIn = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(nextResource);
// BundleEntryTransactionOperationEnum nextResouceOperationIn =
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(nextResource);
// if (nextResouceOperationIn == null && hasValue(ResourceMetadataKeyEnum.DELETED_AT.get(nextResource))) {
// nextResouceOperationIn = BundleEntryTransactionOperationEnum.DELETE;
// }
@ -89,20 +286,24 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
// ResourceTable entity;
// if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.CREATE) {
// entity = null;
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.UPDATE || nextResouceOperationIn == BundleEntryTransactionOperationEnum.DELETE) {
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.UPDATE || nextResouceOperationIn ==
// BundleEntryTransactionOperationEnum.DELETE) {
// if (candidateMatches == null || candidateMatches.size() == 0) {
// if (nextId == null || StringUtils.isBlank(nextId.getIdPart())) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class, "transactionOperationFailedNoId", nextResouceOperationIn.name()));
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationFailedNoId", nextResouceOperationIn.name()));
// }
// entity = tryToLoadEntity(nextId);
// if (entity == null) {
// if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.UPDATE) {
// ourLog.debug("Attempting to UPDATE resource with unknown ID '{}', will CREATE instead", nextId);
// } else if (candidateMatches == null) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class, "transactionOperationFailedUnknownId", nextResouceOperationIn.name(), nextId));
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationFailedUnknownId", nextResouceOperationIn.name(), nextId));
// } else {
// ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(nextResource, BundleEntryTransactionOperationEnum.NOOP);
// ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(nextResource,
// BundleEntryTransactionOperationEnum.NOOP);
// persistedResources.add(null);
// retVal.add(nextResource);
// continue;
@ -111,10 +312,13 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
// } else if (candidateMatches.size() == 1) {
// entity = loadFirstEntityFromCandidateMatches(candidateMatches);
// } else {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class, "transactionOperationWithMultipleMatchFailure", nextResouceOperationIn.name(), matchUrl, candidateMatches.size()));
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationWithMultipleMatchFailure", nextResouceOperationIn.name(), matchUrl,
// candidateMatches.size()));
// }
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.NOOP) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class, "incomingNoopInTransaction"));
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "incomingNoopInTransaction"));
// } else if (nextId.isEmpty()) {
// entity = null;
// } else {
@ -129,7 +333,8 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
// ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
// } else if (nextResouceOperationIn == BundleEntryTransactionOperationEnum.CREATE) {
// if (nextId.isEmpty() == false) {
// ourLog.debug("Resource in transaction has ID[{}] but is marked for CREATE, will ignore ID", nextId.getIdPart());
// ourLog.debug("Resource in transaction has ID[{}] but is marked for CREATE, will ignore ID",
// nextId.getIdPart());
// }
// if (candidateMatches != null) {
// if (candidateMatches.size() == 1) {
@ -142,7 +347,9 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
// continue;
// }
// if (candidateMatches.size() > 1) {
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class, "transactionOperationWithMultipleMatchFailure", BundleEntryTransactionOperationEnum.CREATE.name(), matchUrl, candidateMatches.size()));
// throw new InvalidRequestException(getContext().getLocalizer().getMessage(FhirSystemDaoDstu2.class,
// "transactionOperationWithMultipleMatchFailure", BundleEntryTransactionOperationEnum.CREATE.name(), matchUrl,
// candidateMatches.size()));
// }
// }
// } else {
@ -201,19 +408,28 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
//
// }
//
// for (IResource nextResource : theResources) {
// List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
// for (BaseResourceReferenceDt nextRef : allRefs) {
// IdDt nextId = nextRef.getReference();
// if (idConversions.containsKey(nextId)) {
// IdDt newId = idConversions.get(nextId);
// ourLog.info(" * Replacing resource ref {} with {}", nextId, newId);
// nextRef.setReference(newId);
// } else {
// ourLog.debug(" * Reference [{}] does not exist in bundle", nextId);
// }
// }
// }
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
IResource nextResource = nextOutcome.getResource();
if (nextResource == null) {
continue;
}
List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
for (BaseResourceReferenceDt nextRef : allRefs) {
IdDt nextId = nextRef.getReference();
if (idSubstitutions.containsKey(nextId)) {
IdDt newId = idSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId);
nextRef.setReference(newId);
} else {
ourLog.debug(" * Reference [{}] does not exist in bundle", nextId);
}
}
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false);
}
//
// ourLog.info("Re-flushing updated resource references and extracting search criteria");
//
@ -226,23 +442,94 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao<Bundle> {
//
// InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource);
// Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
// if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(resource) == BundleEntryTransactionOperationEnum.DELETE) {
// if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(resource) ==
// BundleEntryTransactionOperationEnum.DELETE) {
// deletedTimestampOrNull = new Date();
// ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull));
// }
//
// updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull);
// }
//
// long delay = System.currentTimeMillis() - start;
// ourLog.info("Transaction completed in {}ms with {} creations and {} updates", new Object[] { delay, creations, updates });
//
// oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Transaction completed in " + delay + "ms with " + creations + " creations and " + updates + " updates");
//
// notifyWriteCompleted();
return null;
long delay = System.currentTimeMillis() - start;
ourLog.info("Transaction completed in {}ms", new Object[] { delay });
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Transaction completed in " + delay + "ms");
notifyWriteCompleted();
return response;
}
private void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, Entry newEntry) {
IdDt newId = outcome.getId().toUnqualifiedVersionless();
if (newId.equals(nextResourceId) == false) {
idSubstitutions.put(nextResourceId, newId);
}
idToPersistedOutcome.put(newId, outcome);
if (outcome.getCreated().booleanValue()) {
newEntry.getTransactionResponse().setStatus(Long.toString(Constants.STATUS_HTTP_201_CREATED));
} else {
newEntry.getTransactionResponse().setStatus(Long.toString(Constants.STATUS_HTTP_200_OK));
}
newEntry.getTransactionResponse().setLocation(outcome.getId().toUnqualified().getValue());
newEntry.getTransactionResponse().addEtag().setValue(outcome.getId().getVersionIdPart());
}
private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) {
String url = nextEntry.getTransaction().getUrl();
if (isBlank(url)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirSystemDao.class, "transactionMissingUrl", verb.name()));
}
return url;
}
private static class UrlParts {
private IFhirResourceDao<? extends IResource> myDao;
private String myParams;
private String myResourceId;
private String myResourceType;
private String myVersionId;
public IFhirResourceDao<? extends IResource> getDao() {
return myDao;
}
public void setVersionId(String theVersionId) {
myVersionId = theVersionId;
}
public String getVersionId() {
return myVersionId;
}
public String getParams() {
return myParams;
}
public String getResourceId() {
return myResourceId;
}
public String getResourceType() {
return myResourceType;
}
public void setDao(IFhirResourceDao<? extends IResource> theDao) {
myDao = theDao;
}
public void setParams(String theParams) {
myParams = theParams;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
}
}

View File

@ -29,7 +29,6 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -37,9 +36,20 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
void addTag(IdDt theId, String theScheme, String theTerm, String theLabel);
MethodOutcome create(T theResource);
DaoMethodOutcome create(T theResource);
MethodOutcome delete(IdDt theResource);
DaoMethodOutcome create(T theResource, String theIfNoneExist);
/**
* @param thePerformIndexing
* Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work.
*/
DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing);
DaoMethodOutcome delete(IdDt theResource);
DaoMethodOutcome deleteByUrl(String theString);
TagList getAllResourceTags();
@ -57,12 +67,20 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
*
* @param theId
* @return
* @throws ResourceNotFoundException If the ID is not known to the server
* @throws ResourceNotFoundException
* If the ID is not known to the server
*/
T read(IdDt theId);
BaseHasResource readEntity(IdDt theId);
/**
* @param theCheckForForcedId
* If true, this method should fail if the requested ID contains a numeric PID which exists, but is
* obscured by a "forced ID" so should not exist as far as the outside world is concerned.
*/
BaseHasResource readEntity(IdDt theId, boolean theCheckForForcedId);
void removeTag(IdDt theId, String theScheme, String theTerm);
IBundleProvider search(Map<String, IQueryParameterType> theParams);
@ -75,15 +93,17 @@ public interface IFhirResourceDao<T extends IResource> extends IDao {
Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue);
MethodOutcome update(T theResource, IdDt theId);
Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams);
DaoMethodOutcome update(T theResource);
DaoMethodOutcome update(T theResource, String theMatchUrl);
/**
* @param theCheckForForcedId If true, this method should fail if the requested ID contains
* a numeric PID which exists, but is obscured by a "forced ID" so should not exist as
* far as the outside world is concerned.
* @param thePerformIndexing
* Use with caution! If you set this to false, you need to manually perform indexing or your resources
* won't be indexed and searches won't work.
*/
BaseHasResource readEntity(IdDt theId, boolean theCheckForForcedId);
DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing);
}

View File

@ -32,9 +32,15 @@ import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
//@formatter:off
@Entity()
@Table(name = "HFJ_FORCED_ID", uniqueConstraints = { @UniqueConstraint(name = "IDX_FORCEDID", columnNames = { "FORCED_ID" }) })
@NamedQueries(@NamedQuery(name = "Q_GET_FORCED_ID", query = "SELECT f FROM ForcedId f WHERE myForcedId = :ID"))
@Table(name = "HFJ_FORCED_ID", uniqueConstraints = {
@UniqueConstraint(name = "IDX_FORCEDID", columnNames = { "FORCED_ID" })
})
@NamedQueries(value= {
@NamedQuery(name = "Q_GET_FORCED_ID", query = "SELECT f FROM ForcedId f WHERE myForcedId = :ID")
})
//@formatter:on
public class ForcedId {
public static final int MAX_FORCED_ID_LENGTH = 100;

View File

@ -27,6 +27,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
@ -44,11 +45,11 @@ import javax.servlet.http.HttpServletRequest;
public class JpaConformanceProviderDstu1 extends ServerConformanceProvider {
private String myImplementationDescription;
private IFhirSystemDao mySystemDao;
private IFhirSystemDao<List<IResource>> mySystemDao;
private volatile Conformance myCachedValue;
private RestfulServer myRestfulServer;
public JpaConformanceProviderDstu1(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
public JpaConformanceProviderDstu1(RestfulServer theRestfulServer, IFhirSystemDao<List<IResource>> theSystemDao) {
super(theRestfulServer);
myRestfulServer = theRestfulServer;
mySystemDao = theSystemDao;

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
@ -44,11 +45,11 @@ import ca.uhn.fhir.util.ExtensionConstants;
public class JpaConformanceProviderDstu2 extends ServerConformanceProvider {
private String myImplementationDescription;
private IFhirSystemDao mySystemDao;
private IFhirSystemDao<Bundle> mySystemDao;
private volatile Conformance myCachedValue;
private RestfulServer myRestfulServer;
public JpaConformanceProviderDstu2(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
public JpaConformanceProviderDstu2(RestfulServer theRestfulServer, IFhirSystemDao<Bundle> theSystemDao) {
super(theRestfulServer);
myRestfulServer = theRestfulServer;
mySystemDao = theSystemDao;

View File

@ -162,13 +162,12 @@ public class JpaResourceProvider<T extends IResource> extends BaseJpaProvider im
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId) {
startRequest(theRequest);
try {
return myDao.update(theResource, theId);
theResource.setId(theId);
return myDao.update(theResource);
} catch (ResourceNotFoundException e) {
ourLog.info("Can't update resource with ID[" + theId.getValue() + "] because it doesn't exist, going to create it instead");
theResource.setId(theId);
MethodOutcome retVal = myDao.create(theResource);
retVal.setCreated(true);
return retVal;
return myDao.create(theResource);
} finally {
endRequest(theRequest);
}

View File

@ -1,17 +1,7 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Date;
@ -40,6 +30,7 @@ import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Device;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu2.resource.Encounter;
@ -47,7 +38,9 @@ import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.dstu2.valueset.QuantityComparatorEnum;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
@ -65,8 +58,10 @@ import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
@ -84,6 +79,150 @@ public class FhirResourceDaoTest {
private static IFhirResourceDao<Organization> ourOrganizationDao;
private static IFhirResourceDao<Patient> ourPatientDao;
@Test
public void testCreateDuplicateIdFails() {
String methodName = "testCreateDuplocateIdFailsText";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
try {
ourPatientDao.create(p);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Can not create entity with ID[" + methodName + "], a resource with this ID already exists"));
}
}
@Test
public void testUpdateByUrl() {
String methodName = "testUpdateByUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ourPatientDao.update(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
p = ourPatientDao.read(id.toVersionless());
assertThat(p.getId().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
}
@Test
public void testCreateNumericIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/123");
try {
ourPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Can not create entity with ID[123], this server does not allow clients to assign numeric IDs"));
}
}
@Test
public void testDeleteWithMatchUrl() {
String methodName = "testDeleteWithMatchUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
Bundle request = new Bundle();
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
ourPatientDao.deleteByUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
try {
ourPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
try {
ourPatientDao.read(new IdDt("Patient/" + methodName));
fail();
} catch (ResourceNotFoundException e) {
// ok
}
IBundleProvider history = ourPatientDao.history(id, null);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)));
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)).getValue());
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(1,1).get(0)));
}
@Test
public void testCreateWithIfNoneExist() {
String methodName = "testCreateWithIfNoneExist";
MethodOutcome results;
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
results = ourPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
assertEquals(id.getIdPart(), results.getId().getIdPart());
assertFalse(results.getCreated().booleanValue());
// Now create a second one
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName + "DOESNTEXIST");
results = ourPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName + "DOESNTEXIST");
assertNotEquals(id.getIdPart(), results.getId().getIdPart());
assertTrue(results.getCreated().booleanValue());
// Now try to create one with the original match URL and it should fail
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
try {
ourPatientDao.create(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
fail();
} catch (PreconditionFailedException e) {
assertThat(e.getMessage(), containsString("Failed to CREATE"));
}
}
@Test
public void testChoiceParamConcept() {
Observation o1 = new Observation();
@ -329,7 +468,7 @@ public class FhirResourceDaoTest {
{
Patient patient = ourPatientDao.read(id2);
patient.addIdentifier().setSystem("ZZZZZZZ").setValue("ZZZZZZZZZ");
id2b = ourPatientDao.update(patient, id2).getId();
id2b = ourPatientDao.update(patient).getId();
}
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] { id1, id2, id2b });
@ -641,7 +780,6 @@ public class FhirResourceDaoTest {
assertTrue(patients.size() >= 2);
}
@Test
public void testHistoryByForcedId() {
IdDt idv1;
@ -654,7 +792,8 @@ public class FhirResourceDaoTest {
idv1 = ourPatientDao.create(patient).getId();
patient.addName().addFamily("Tester").addGiven("testHistoryByForcedIdName2");
idv2 = ourPatientDao.update(patient, idv1.toUnqualifiedVersionless()).getId();
patient.setId(patient.getId().toUnqualifiedVersionless());
idv2 = ourPatientDao.update(patient).getId();
}
List<Patient> patients = toList(ourPatientDao.history(idv1.toVersionless(), null));
@ -1477,7 +1616,7 @@ public class FhirResourceDaoTest {
Thread.sleep(1000);
retrieved.getIdentifierFirstRep().setValue("002");
MethodOutcome outcome2 = ourPatientDao.update(retrieved, outcome.getId());
MethodOutcome outcome2 = ourPatientDao.update(retrieved);
assertEquals(outcome.getId().getIdPart(), outcome2.getId().getIdPart());
assertNotEquals(outcome.getId().getVersionIdPart(), outcome2.getId().getVersionIdPart());
@ -1536,7 +1675,7 @@ public class FhirResourceDaoTest {
// Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
MethodOutcome update2 = ourPatientDao.update(p1, p1id);
MethodOutcome update2 = ourPatientDao.update(p1);
IdDt p1id2 = update2.getId();
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
@ -1564,14 +1703,16 @@ public class FhirResourceDaoTest {
Organization p2 = new Organization();
p2.getNameElement().setValue("testUpdateRejectsInvalidTypes");
try {
ourOrganizationDao.update(p2, new IdDt("Organization/" + p1id.getIdPart()));
p2.setId(new IdDt("Organization/" + p1id.getIdPart()));
ourOrganizationDao.update(p2);
fail();
} catch (UnprocessableEntityException e) {
// good
}
try {
ourOrganizationDao.update(p2, new IdDt("Patient/" + p1id.getIdPart()));
p2.setId(new IdDt("Patient/" + p1id.getIdPart()));
ourOrganizationDao.update(p2);
fail();
} catch (UnprocessableEntityException e) {
// good
@ -1602,7 +1743,8 @@ public class FhirResourceDaoTest {
}
try {
ourPatientDao.update(p1, new IdDt("Patient/" + p1longId));
p1.setId(new IdDt("Patient/" + p1longId));
ourPatientDao.update(p1);
fail();
} catch (ResourceNotFoundException e) {
// good
@ -1619,7 +1761,8 @@ public class FhirResourceDaoTest {
assertEquals("testReadVorcedIdVersionHistory", p1id.getIdPart());
p1.addIdentifier().setSystem("urn:system").setValue("testReadVorcedIdVersionHistory02");
IdDt p1idv2 = ourPatientDao.update(p1, p1id).getId();
p1.setId(p1id);
IdDt p1idv2 = ourPatientDao.update(p1).getId();
assertEquals("testReadVorcedIdVersionHistory", p1idv2.getIdPart());
assertNotEquals(p1id.getValue(), p1idv2.getValue());

View File

@ -0,0 +1,414 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Location;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class FhirSystemDaoDstu1Test {
private static ClassPathXmlApplicationContext ourCtx;
private static FhirContext ourFhirContext;
private static IFhirResourceDao<Location> ourLocationDao;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu1Test.class);
private static IFhirResourceDao<Observation> ourObservationDao;
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<List<IResource>> ourSystemDao;
@Test
public void testGetResourceCounts() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testGetResourceCountsO01");
ourObservationDao.create(obs);
Map<String, Long> oldCounts = ourSystemDao.getResourceCounts();
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("testGetResourceCountsP01");
patient.addName().addFamily("Tester").addGiven("Joe");
ourPatientDao.create(patient);
Map<String, Long> newCounts = ourSystemDao.getResourceCounts();
if (oldCounts.containsKey("Patient")) {
assertEquals(oldCounts.get("Patient") + 1, (long) newCounts.get("Patient"));
} else {
assertEquals(1L, (long) newCounts.get("Patient"));
}
assertEquals((long) oldCounts.get("Observation"), (long) newCounts.get("Observation"));
}
@Test
public void testHistory() throws Exception {
Date start = new Date();
Thread.sleep(10);
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("testHistory");
patient.addName().addFamily("Tester").addGiven("Joe");
IdDt pid = ourPatientDao.create(patient).getId().toVersionless();
Thread.sleep(10);
patient.setId(pid);
IdDt newpid = ourPatientDao.update(patient).getId();
Thread.sleep(10);
patient.setId(pid);
IdDt newpid2 = ourPatientDao.update(patient).getId();
Thread.sleep(10);
patient.setId(pid);
IdDt newpid3 = ourPatientDao.update(patient).getId();
IBundleProvider values = ourSystemDao.history(start);
assertEquals(4, values.size());
List<IResource> res = values.getResources(0, 4);
assertEquals(newpid3, res.get(0).getId());
assertEquals(newpid2, res.get(1).getId());
assertEquals(newpid, res.get(2).getId());
assertEquals(pid.toUnqualifiedVersionless(), res.get(3).getId().toUnqualifiedVersionless());
Location loc = new Location();
loc.getAddress().addLine("AAA");
IdDt lid = ourLocationDao.create(loc).getId();
Location loc2 = new Location();
loc2.getAddress().addLine("AAA");
ourLocationDao.create(loc2).getId();
Thread.sleep(2000);
values = ourLocationDao.history(start);
assertEquals(2, values.size());
values = ourLocationDao.history(lid.getIdPartAsLong(), start);
assertEquals(1, values.size());
}
@Test
public void testPersistWithSimpleLink() {
Patient patient = new Patient();
patient.setId(new IdDt("Patient/testPersistWithSimpleLinkP01"));
patient.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP01");
patient.addName().addFamily("Tester").addGiven("Joe");
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/testPersistWithSimpleLinkP01"));
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
String patientId = (patient.getId().getIdPart());
String obsId = (obs.getId().getIdPart());
// assertThat(patientId, greaterThan(0L));
// assertEquals(patientVersion, 1L);
// assertThat(obsId, greaterThan(patientId));
// assertEquals(obsVersion, 1L);
// Try to search
IBundleProvider obsResults = ourObservationDao.search(Observation.SP_NAME, new IdentifierDt("urn:system", "testPersistWithSimpleLinkO01"));
assertEquals(1, obsResults.size());
IBundleProvider patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01"));
assertEquals(1, obsResults.size());
IdDt foundPatientId = patResults.getResources(0, 1).get(0).getId();
ResourceReferenceDt subject = obs.getSubject();
assertEquals(foundPatientId.getIdPart(), subject.getReference().getIdPart());
// Update
patient = (Patient) patResults.getResources(0, 1).get(0);
obs = (Observation) obsResults.getResources(0, 1).get(0);
patient.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP02");
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO02");
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
String patientId2 = (patient.getId().getIdPart());
String patientVersion2 = (patient.getId().getVersionIdPart());
String obsId2 = (obs.getId().getIdPart());
String obsVersion2 = (obs.getId().getVersionIdPart());
assertEquals(patientId, patientId2);
assertEquals(patientVersion2, "2");
assertEquals(obsId, obsId2);
assertEquals(obsVersion2, "2");
}
@Test
public void testPersistWithUnknownId() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/999998888888"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/999998888888 not found, specified in path: Observation.subject"));
}
obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/1.2.3.4"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/1.2.3.4 not found, specified in path: Observation.subject"));
}
}
@Test
public void testTagOperationss() throws Exception {
TagList preSystemTl = ourSystemDao.getAllTags();
TagList tl1 = new TagList();
tl1.addTag("testGetAllTagsScheme1", "testGetAllTagsTerm1", "testGetAllTagsLabel1");
Patient p1 = new Patient();
p1.addIdentifier().setSystem("foo").setValue("testGetAllTags01");
ResourceMetadataKeyEnum.TAG_LIST.put(p1, tl1);
ourPatientDao.create(p1);
TagList tl2 = new TagList();
tl2.addTag("testGetAllTagsScheme2", "testGetAllTagsTerm2", "testGetAllTagsLabel2");
Observation o1 = new Observation();
o1.getName().setText("testGetAllTags02");
ResourceMetadataKeyEnum.TAG_LIST.put(o1, tl2);
IdDt o1id = ourObservationDao.create(o1).getId();
assertTrue(o1id.getVersionIdPart() != null);
TagList postSystemTl = ourSystemDao.getAllTags();
assertEquals(preSystemTl.size() + 2, postSystemTl.size());
assertEquals("testGetAllTagsLabel1", postSystemTl.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
TagList tags = ourPatientDao.getAllResourceTags();
assertEquals("testGetAllTagsLabel1", tags.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
assertNull(tags.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
TagList tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
o1.getResourceMetadata().remove(ResourceMetadataKeyEnum.TAG_LIST);
o1.setId(o1id);
IdDt o1id2 = ourObservationDao.update(o1).getId();
assertTrue(o1id2.getVersionIdPart() != null);
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Remove a tag from a version
*/
ourObservationDao.removeTag(o1id2, "testGetAllTagsScheme2", "testGetAllTagsTerm2");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Add a tag
*/
ourObservationDao.addTag(o1id2, "testGetAllTagsScheme3", "testGetAllTagsTerm3", "testGetAllTagsLabel3");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
assertNotNull(tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3"));
assertEquals("testGetAllTagsLabel3", tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3").getLabel());
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
}
@Test(expected = InvalidRequestException.class)
public void testTransactionFailsWithDuplicateIds() {
Patient patient1 = new Patient();
patient1.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient1.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP01");
Patient patient2 = new Patient();
patient2.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient2.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP02");
ourSystemDao.transaction(Arrays.asList((IResource) patient1, patient2));
}
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = FhirSystemDaoDstu1Test.class.getResourceAsStream("/bundle-dstu1.xml");
Bundle bundle = ourFhirContext.newXmlParser().parseBundle(new InputStreamReader(bundleRes));
List<IResource> res = bundle.toListOfResources();
ourSystemDao.transaction(res);
Patient p1 = (Patient) res.get(0);
String id = p1.getId().getValue();
ourLog.info("ID: {}", id);
assertThat(id, not(equalToIgnoringCase("74635")));
assertThat(id, not(equalToIgnoringCase("")));
}
/**
* Issue #55
*/
@Test
public void testTransactionWithCidIds() throws Exception {
List<IResource> res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.setId("cid:patient1");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithCidIds01");
res.add(p1);
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds02");
o1.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
res.add(o1);
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds03");
o2.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
res.add(o2);
ourSystemDao.transaction(res);
assertTrue(p1.getId().getValue(), p1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o1.getId().getValue(), o1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o2.getId().getValue(), o2.getId().getIdPart().matches("^[0-9]+$"));
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
@Test
public void testTransactionWithDelete() throws Exception {
/*
* Create 3
*/
List<IResource> res;
res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p1);
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p2);
Patient p3 = new Patient();
p3.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p3);
ourSystemDao.transaction(res);
/*
* Verify
*/
IBundleProvider results = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(3, results.size());
/*
* Now delete 2
*/
res = new ArrayList<IResource>();
List<IResource> existing = results.getResources(0, 3);
p1 = new Patient();
p1.setId(existing.get(0).getId());
ResourceMetadataKeyEnum.DELETED_AT.put(p1, InstantDt.withCurrentTime());
res.add(p1);
p2 = new Patient();
p2.setId(existing.get(1).getId());
ResourceMetadataKeyEnum.DELETED_AT.put(p2, InstantDt.withCurrentTime());
res.add(p2);
ourSystemDao.transaction(res);
/*
* Verify
*/
IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(1, results2.size());
List<IResource> existing2 = results2.getResources(0, 1);
assertEquals(existing2.get(0).getId(), existing.get(2).getId());
}
@AfterClass
public static void afterClass() {
ourCtx.close();
}
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {
ourCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu1.xml", "fhir-jpabase-spring-test-config.xml");
ourFhirContext = ourCtx.getBean(FhirContext.class);
ourPatientDao = ourCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class);
ourObservationDao = ourCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class);
ourLocationDao = ourCtx.getBean("myLocationDaoDstu1", IFhirResourceDao.class);
ourSystemDao = ourCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
}
}

View File

@ -0,0 +1,681 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionOperationEnum;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class FhirSystemDaoDstu2Test {
private static ClassPathXmlApplicationContext ourCtx;
private static FhirContext ourFhirContext;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<Bundle> ourSystemDao;
private static IFhirResourceDao<Observation> ourObservationDao;
@Test
public void testTransactionCreateMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateMatchUrlWithOneMatch";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
Entry respEntry = resp.getEntry().get(1);
assertEquals(Constants.STATUS_HTTP_200_OK + "", respEntry.getTransactionResponse().getStatus());
assertThat(respEntry.getTransactionResponse().getLocation(), endsWith("Patient/" + id.getIdPart() + "/_history/1"));
assertEquals("1", respEntry.getTransactionResponse().getEtag().get(0).getValue());
respEntry = resp.getEntry().get(2);
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getTransactionResponse().getStatus());
assertThat(respEntry.getTransactionResponse().getLocation(), containsString("Observation/"));
assertThat(respEntry.getTransactionResponse().getLocation(), endsWith("/_history/1"));
assertEquals("1", respEntry.getTransactionResponse().getEtag().get(0).getValue());
o = (Observation) ourObservationDao.read(new IdDt(respEntry.getTransactionResponse().getLocationElement()));
assertEquals(id.toVersionless(), o.getSubject().getReference());
assertEquals("1", o.getId().getVersionIdPart());
}
@Test
public void testTransactionReadAndSearch() {
String methodName = "testTransactionReadAndSearch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt idv1 = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got id: {}", idv1);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Family Name");
p.setId("Patient/" + methodName);
IdDt idv2 = ourPatientDao.update(p).getId();
ourLog.info("Updated patient, got id: {}", idv2);
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualifiedVersionless().getValue());
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualified().getValue());
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.GET).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(4, resp.getEntry().size());
Entry nextEntry;
nextEntry = resp.getEntry().get(1);
assertEquals(Patient.class, nextEntry.getResource().getClass());
assertEquals(idv2.toUnqualified(), nextEntry.getResource().getId().toUnqualified());
nextEntry = resp.getEntry().get(2);
assertEquals(Patient.class, nextEntry.getResource().getClass());
assertEquals(idv1.toUnqualified(), nextEntry.getResource().getId().toUnqualified());
nextEntry = resp.getEntry().get(3);
assertEquals(Bundle.class, nextEntry.getResource().getClass());
Bundle respBundle = (Bundle)nextEntry.getResource();
assertEquals(1, respBundle.getTotal().intValue());
}
@Test
public void testTransactionCreateMatchUrlWithTwoMatch() {
String methodName = "testTransactionCreateMatchUrlWithTwoMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
Bundle request = new Bundle();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
try {
ourSystemDao.transaction(request);
fail();
} catch (PreconditionFailedException e) {
assertThat(e.getMessage(), containsString("with match URL \"Patient"));
}
}
@Test
public void testTransactionCreateMatchUrlWithZeroMatch() {
String methodName = "testTransactionCreateMatchUrlWithZeroMatch";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
Entry respEntry = resp.getEntry().get(1);
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getTransactionResponse().getStatus());
String patientId = respEntry.getTransactionResponse().getLocation();
assertThat(patientId, not(endsWith("Patient/" + methodName + "/_history/1")));
assertThat(patientId, (endsWith("/_history/1")));
assertThat(patientId, (containsString("Patient/")));
assertEquals("1", respEntry.getTransactionResponse().getEtag().get(0).getValue());
respEntry = resp.getEntry().get(2);
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getTransactionResponse().getStatus());
assertThat(respEntry.getTransactionResponse().getLocation(), containsString("Observation/"));
assertThat(respEntry.getTransactionResponse().getLocation(), endsWith("/_history/1"));
assertEquals("1", respEntry.getTransactionResponse().getEtag().get(0).getValue());
o = (Observation) ourObservationDao.read(new IdDt(respEntry.getTransactionResponse().getLocationElement()));
assertEquals(new IdDt(patientId).toUnqualifiedVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionCreateNoMatchUrl() {
String methodName = "testTransactionCreateNoMatchUrl";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(2, resp.getEntry().size());
Entry respEntry = resp.getEntry().get(1);
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getTransactionResponse().getStatus());
String patientId = respEntry.getTransactionResponse().getLocation();
assertThat(patientId, not(containsString("test")));
}
@Test
public void testTransactionDeleteMatchUrlWithOneMatch() {
String methodName = "testTransactionDeleteMatchUrlWithOneMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(2, resp.getEntry().size());
Entry nextEntry = resp.getEntry().get(1);
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + "", nextEntry.getTransactionResponse().getStatus());
try {
ourPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
try {
ourPatientDao.read(new IdDt("Patient/" + methodName));
fail();
} catch (ResourceNotFoundException e) {
// ok
}
IBundleProvider history = ourPatientDao.history(id, null);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)));
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)).getValue());
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(1, 1).get(0)));
}
@Test
public void testTransactionDeleteMatchUrlWithTwoMatch() {
String methodName = "testTransactionDeleteMatchUrlWithTwoMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
try {
ourSystemDao.transaction(request);
fail();
} catch (ResourceNotFoundException e) {
assertThat(e.getMessage(), containsString("resource with match URL \"Patient?"));
}
}
@Test
public void testTransactionDeleteByResourceId() {
String methodName = "testTransactionDeleteByResourceId";
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id1 = ourPatientDao.create(p1).getId();
ourLog.info("Created patient, got it: {}", id1);
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue(methodName);
p2.setId("Patient/" + methodName);
IdDt id2 = ourPatientDao.create(p2).getId();
ourLog.info("Created patient, got it: {}", id2);
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient/" + id1.getIdPart());
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient/" + id2.getIdPart());
ourPatientDao.read(id1.toVersionless());
ourPatientDao.read(id2.toVersionless());
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
assertEquals("204", resp.getEntry().get(1).getTransactionResponse().getStatus());
assertEquals("204", resp.getEntry().get(2).getTransactionResponse().getStatus());
try {
ourPatientDao.read(id1.toVersionless());
fail();
} catch (ResourceGoneException e) {
// good
}
try {
ourPatientDao.read(id2.toVersionless());
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Test
public void testTransactionDeleteMatchUrlWithZeroMatch() {
String methodName = "testTransactionDeleteMatchUrlWithZeroMatch";
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
try {
ourSystemDao.transaction(request);
fail();
} catch (ResourceNotFoundException e) {
assertThat(e.getMessage(), containsString("resource matching URL \"Patient?"));
}
}
@Test
public void testTransactionDeleteNoMatchUrl() {
String methodName = "testTransactionDeleteNoMatchUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
Bundle request = new Bundle();
request.addEntry().getTransaction().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Bundle res = ourSystemDao.transaction(request);
assertEquals(2, res.getEntry().size());
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + "", res.getEntry().get(1).getTransactionResponse().getStatus());
try {
ourPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
}
@Test(expected = InvalidRequestException.class)
public void testTransactionFailsWithDuplicateIds() {
Bundle request = new Bundle();
Patient patient1 = new Patient();
patient1.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient1.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP01");
request.addEntry().setResource(patient1).getTransaction().setMethod(HTTPVerbEnum.POST);
Patient patient2 = new Patient();
patient2.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient2.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP02");
request.addEntry().setResource(patient2).getTransaction().setMethod(HTTPVerbEnum.POST);
ourSystemDao.transaction(request);
}
@Test
public void testTransactionUpdateMatchUrlWithOneMatch() {
String methodName = "testTransactionUpdateMatchUrlWithOneMatch";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.PUT).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
Entry nextEntry = resp.getEntry().get(1);
assertEquals("200", nextEntry.getTransactionResponse().getStatus());
assertThat(nextEntry.getTransactionResponse().getLocation(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
nextEntry = resp.getEntry().get(1);
assertEquals(""+Constants.STATUS_HTTP_200_OK, nextEntry.getTransactionResponse().getStatus());
assertThat(nextEntry.getTransactionResponse().getLocation(), not(emptyString()));
nextEntry = resp.getEntry().get(2);
o = ourObservationDao.read(new IdDt(nextEntry.getTransactionResponse().getLocation()));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateMatchUrlWithTwoMatch() {
String methodName = "testTransactionUpdateMatchUrlWithTwoMatch";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.PUT).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
try {
ourSystemDao.transaction(request);
fail();
} catch (PreconditionFailedException e) {
assertThat(e.getMessage(), containsString("with match URL \"Patient"));
}
}
@Test
public void testTransactionUpdateMatchUrlWithZeroMatch() {
String methodName = "testTransactionUpdateMatchUrlWithZeroMatch";
Bundle request = new Bundle();
Patient p = new Patient();
p.addName().addFamily("Hello");
IdDt id = ourPatientDao.create(p).getId();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId(id);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.PUT).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference(id);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
Entry nextEntry = resp.getEntry().get(1);
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", nextEntry.getTransactionResponse().getStatus());
assertThat(nextEntry.getTransactionResponse().getLocation(), not(containsString("test")));
assertNotEquals(id.toVersionless(), new IdDt(nextEntry.getTransactionResponse().getLocation()).toVersionless());
assertThat(nextEntry.getTransactionResponse().getLocation(), endsWith("/_history/1"));
nextEntry = resp.getEntry().get(1);
assertEquals(""+Constants.STATUS_HTTP_201_CREATED, nextEntry.getTransactionResponse().getStatus());
nextEntry = resp.getEntry().get(2);
o = ourObservationDao.read(new IdDt(nextEntry.getTransactionResponse().getLocation()));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateNoMatchUrl() {
String methodName = "testTransactionUpdateNoMatchUrl";
Bundle request = new Bundle();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
request.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.PUT).setUrl("Patient/"+ id.getIdPart());
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
request.addEntry().setResource(o).getTransaction().setMethod(HTTPVerbEnum.POST);
Bundle resp = ourSystemDao.transaction(request);
assertEquals(3, resp.getEntry().size());
Entry nextEntry = resp.getEntry().get(1);
assertEquals("200", nextEntry.getTransactionResponse().getStatus());
assertThat(nextEntry.getTransactionResponse().getLocation(), (containsString("test")));
assertEquals(id.toVersionless(), new IdDt(nextEntry.getTransactionResponse().getLocation()).toVersionless());
assertNotEquals(id, new IdDt(nextEntry.getTransactionResponse().getLocation()));
assertThat(nextEntry.getTransactionResponse().getLocation(), endsWith("/_history/2"));
nextEntry = resp.getEntry().get(2);
assertEquals(""+Constants.STATUS_HTTP_201_CREATED, nextEntry.getTransactionResponse().getStatus());
o = ourObservationDao.read(new IdDt(resp.getEntry().get(2).getTransactionResponse().getLocation()));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
//
//
// /**
// * Issue #55
// */
// @Test
// public void testTransactionWithCidIds() throws Exception {
// Bundle request = new Bundle();
//
// Patient p1 = new Patient();
// p1.setId("cid:patient1");
// p1.addIdentifier().setSystem("system").setValue("testTransactionWithCidIds01");
// res.add(p1);
//
// Observation o1 = new Observation();
// o1.setId("cid:observation1");
// o1.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds02");
// o1.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
// res.add(o1);
//
// Observation o2 = new Observation();
// o2.setId("cid:observation2");
// o2.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds03");
// o2.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
// res.add(o2);
//
// ourSystemDao.transaction(res);
//
// assertTrue(p1.getId().getValue(), p1.getId().getIdPart().matches("^[0-9]+$"));
// assertTrue(o1.getId().getValue(), o1.getId().getIdPart().matches("^[0-9]+$"));
// assertTrue(o2.getId().getValue(), o2.getId().getIdPart().matches("^[0-9]+$"));
//
// assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
// assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
//
// }
//
// @Test
// public void testTransactionWithDelete() throws Exception {
// Bundle request = new Bundle();
//
// /*
// * Create 3
// */
//
// List<IResource> res;
// res = new ArrayList<IResource>();
//
// Patient p1 = new Patient();
// p1.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
// res.add(p1);
//
// Patient p2 = new Patient();
// p2.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
// res.add(p2);
//
// Patient p3 = new Patient();
// p3.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
// res.add(p3);
//
// ourSystemDao.transaction(res);
//
// /*
// * Verify
// */
//
// IBundleProvider results = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system",
// "testTransactionWithDelete"));
// assertEquals(3, results.size());
//
// /*
// * Now delete 2
// */
//
// request = new Bundle();
// res = new ArrayList<IResource>();
// List<IResource> existing = results.getResources(0, 3);
//
// p1 = new Patient();
// p1.setId(existing.get(0).getId());
// ResourceMetadataKeyEnum.DELETED_AT.put(p1, InstantDt.withCurrentTime());
// res.add(p1);
//
// p2 = new Patient();
// p2.setId(existing.get(1).getId());
// ResourceMetadataKeyEnum.DELETED_AT.put(p2, InstantDt.withCurrentTime());
// res.add(p2);
//
// ourSystemDao.transaction(res);
//
// /*
// * Verify
// */
//
// IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system",
// "testTransactionWithDelete"));
// assertEquals(1, results2.size());
// List<IResource> existing2 = results2.getResources(0, 1);
// assertEquals(existing2.get(0).getId(), existing.get(2).getId());
//
// }
@AfterClass
public static void afterClass() {
ourCtx.close();
}
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {
ourCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu2.xml", "fhir-jpabase-spring-test-config.xml");
ourFhirContext = ourCtx.getBean(FhirContext.class);
assertEquals(FhirVersionEnum.DSTU2, ourFhirContext.getVersion().getVersion());
ourPatientDao = ourCtx.getBean("myPatientDaoDstu2", IFhirResourceDao.class);
ourObservationDao = ourCtx.getBean("myObservationDaoDstu2", IFhirResourceDao.class);
ourSystemDao = ourCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
}
}

View File

@ -1,899 +0,0 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Location;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionOperationEnum;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class FhirSystemDaoTest {
private static ClassPathXmlApplicationContext ourCtx;
private static FhirContext ourFhirContext;
private static IFhirResourceDao<Location> ourLocationDao;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoTest.class);
private static IFhirResourceDao<Observation> ourObservationDao;
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<List<IResource>> ourSystemDao;
@Test
public void testGetResourceCounts() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testGetResourceCountsO01");
ourObservationDao.create(obs);
Map<String, Long> oldCounts = ourSystemDao.getResourceCounts();
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("testGetResourceCountsP01");
patient.addName().addFamily("Tester").addGiven("Joe");
ourPatientDao.create(patient);
Map<String, Long> newCounts = ourSystemDao.getResourceCounts();
if (oldCounts.containsKey("Patient")) {
assertEquals(oldCounts.get("Patient") + 1, (long) newCounts.get("Patient"));
} else {
assertEquals(1L, (long) newCounts.get("Patient"));
}
assertEquals((long) oldCounts.get("Observation"), (long) newCounts.get("Observation"));
}
@Test
public void testHistory() throws Exception {
Date start = new Date();
Thread.sleep(10);
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("testHistory");
patient.addName().addFamily("Tester").addGiven("Joe");
IdDt pid = ourPatientDao.create(patient).getId().toVersionless();
Thread.sleep(10);
IdDt newpid = ourPatientDao.update(patient, pid).getId();
Thread.sleep(10);
IdDt newpid2 = ourPatientDao.update(patient, pid).getId();
Thread.sleep(10);
IdDt newpid3 = ourPatientDao.update(patient, pid).getId();
IBundleProvider values = ourSystemDao.history(start);
assertEquals(4, values.size());
List<IResource> res = values.getResources(0, 4);
assertEquals(newpid3, res.get(0).getId());
assertEquals(newpid2, res.get(1).getId());
assertEquals(newpid, res.get(2).getId());
assertEquals(pid.toUnqualifiedVersionless(), res.get(3).getId().toUnqualifiedVersionless());
Location loc = new Location();
loc.getAddress().addLine("AAA");
IdDt lid = ourLocationDao.create(loc).getId();
Location loc2 = new Location();
loc2.getAddress().addLine("AAA");
ourLocationDao.create(loc2).getId();
Thread.sleep(2000);
values = ourLocationDao.history(start);
assertEquals(2, values.size());
values = ourLocationDao.history(lid.getIdPartAsLong(), start);
assertEquals(1, values.size());
}
@Test
public void testPersistWithSimpleLink() {
Patient patient = new Patient();
patient.setId(new IdDt("Patient/testPersistWithSimpleLinkP01"));
patient.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP01");
patient.addName().addFamily("Tester").addGiven("Joe");
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/testPersistWithSimpleLinkP01"));
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
String patientId = (patient.getId().getIdPart());
String obsId = (obs.getId().getIdPart());
// assertThat(patientId, greaterThan(0L));
// assertEquals(patientVersion, 1L);
// assertThat(obsId, greaterThan(patientId));
// assertEquals(obsVersion, 1L);
// Try to search
IBundleProvider obsResults = ourObservationDao.search(Observation.SP_NAME, new IdentifierDt("urn:system", "testPersistWithSimpleLinkO01"));
assertEquals(1, obsResults.size());
IBundleProvider patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01"));
assertEquals(1, obsResults.size());
IdDt foundPatientId = patResults.getResources(0, 1).get(0).getId();
ResourceReferenceDt subject = obs.getSubject();
assertEquals(foundPatientId.getIdPart(), subject.getReference().getIdPart());
// Update
patient = (Patient) patResults.getResources(0, 1).get(0);
obs = (Observation) obsResults.getResources(0, 1).get(0);
patient.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP02");
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO02");
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
String patientId2 = (patient.getId().getIdPart());
String patientVersion2 = (patient.getId().getVersionIdPart());
String obsId2 = (obs.getId().getIdPart());
String obsVersion2 = (obs.getId().getVersionIdPart());
assertEquals(patientId, patientId2);
assertEquals(patientVersion2, "2");
assertEquals(obsId, obsId2);
assertEquals(obsVersion2, "2");
}
@Test
public void testPersistWithUnknownId() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/999998888888"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/999998888888 not found, specified in path: Observation.subject"));
}
obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/1.2.3.4"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/1.2.3.4 not found, specified in path: Observation.subject"));
}
}
@Test
public void testTagOperationss() throws Exception {
TagList preSystemTl = ourSystemDao.getAllTags();
TagList tl1 = new TagList();
tl1.addTag("testGetAllTagsScheme1", "testGetAllTagsTerm1", "testGetAllTagsLabel1");
Patient p1 = new Patient();
p1.addIdentifier().setSystem("foo").setValue("testGetAllTags01");
ResourceMetadataKeyEnum.TAG_LIST.put(p1, tl1);
ourPatientDao.create(p1);
TagList tl2 = new TagList();
tl2.addTag("testGetAllTagsScheme2", "testGetAllTagsTerm2", "testGetAllTagsLabel2");
Observation o1 = new Observation();
o1.getName().setText("testGetAllTags02");
ResourceMetadataKeyEnum.TAG_LIST.put(o1, tl2);
IdDt o1id = ourObservationDao.create(o1).getId();
assertTrue(o1id.getVersionIdPart() != null);
TagList postSystemTl = ourSystemDao.getAllTags();
assertEquals(preSystemTl.size() + 2, postSystemTl.size());
assertEquals("testGetAllTagsLabel1", postSystemTl.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
TagList tags = ourPatientDao.getAllResourceTags();
assertEquals("testGetAllTagsLabel1", tags.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
assertNull(tags.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
TagList tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
o1.getResourceMetadata().remove(ResourceMetadataKeyEnum.TAG_LIST);
IdDt o1id2 = ourObservationDao.update(o1, o1id).getId();
assertTrue(o1id2.getVersionIdPart() != null);
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Remove a tag from a version
*/
ourObservationDao.removeTag(o1id2, "testGetAllTagsScheme2", "testGetAllTagsTerm2");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Add a tag
*/
ourObservationDao.addTag(o1id2, "testGetAllTagsScheme3", "testGetAllTagsTerm3", "testGetAllTagsLabel3");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
assertNotNull(tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3"));
assertEquals("testGetAllTagsLabel3", tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3").getLabel());
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
}
@Test
public void testTransactionCreateMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateMatchUrlWithOneMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.CREATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.NOOP, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertEquals(id, p.getId());
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionCreateMatchUrlWithTwoMatch() {
String methodName = "testTransactionCreateMatchUrlWithTwoMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.CREATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
try {
ourSystemDao.transaction(Arrays.asList((IResource) p, o));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("with match URL \"Patient"));
}
}
@Test
public void testTransactionCreateMatchUrlWithZeroMatch() {
String methodName = "testTransactionCreateMatchUrlWithZeroMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.CREATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().getIdPart(), not(containsString("test")));
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(p.getId().toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionCreateNoMatchUrl() {
String methodName = "testTransactionCreateNoMatchUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.CREATE);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p));
assertEquals(2, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().getIdPart(), not(containsString("test")));
}
@Test
public void testTransactionDeleteMatchUrlWithOneMatch() {
String methodName = "testTransactionDeleteMatchUrlWithOneMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.DELETE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p));
assertEquals(2, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.DELETE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
try {
ourPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
try {
ourPatientDao.read(new IdDt("Patient/" + methodName));
fail();
} catch (ResourceNotFoundException e) {
// ok
}
}
@Test
public void testTransactionDeleteMatchUrlWithTwoMatch() {
String methodName = "testTransactionDeleteMatchUrlWithTwoMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.DELETE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
try {
ourSystemDao.transaction(Arrays.asList((IResource) p, o));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("with match URL \"Patient"));
}
}
@Test
public void testTransactionDeleteMatchUrlWithZeroMatch() {
String methodName = "testTransactionDeleteMatchUrlWithZeroMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName + "ZZZ");
p.addName().addFamily("Hello");
IdDt id = ourPatientDao.create(p).getId();
p = new Patient();
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
IdDt id2 = ourPatientDao.create(p).getId();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.DELETE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p));
assertEquals(2, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.DELETE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().toVersionless().toString(), (containsString("test")));
assertThat(p.getId().toString(), endsWith("/_history/2"));
assertEquals(id2.toVersionless(), p.getId().toVersionless());
assertNotEquals(id2, p.getId());
try {
ourPatientDao.read(id2.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
Patient found = ourPatientDao.read(id);
assertEquals(id, found.getId());
}
@Test
public void testTransactionDeleteNoMatchUrl() {
String methodName = "testTransactionDeleteNoMatchUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.DELETE);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p));
assertEquals(2, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.DELETE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(p).getValue());
try {
ourPatientDao.read(id.toVersionless());
fail();
} catch (ResourceGoneException e) {
// ok
}
}
@Test(expected = InvalidRequestException.class)
public void testTransactionFailsWithDuplicateIds() {
Patient patient1 = new Patient();
patient1.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient1.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP01");
Patient patient2 = new Patient();
patient2.setId(new IdDt("Patient/testTransactionFailsWithDusplicateIds"));
patient2.addIdentifier().setSystem("urn:system").setValue("testPersistWithSimpleLinkP02");
ourSystemDao.transaction(Arrays.asList((IResource) patient1, patient2));
}
// @Test TODO: re-enable
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = FhirSystemDaoTest.class.getResourceAsStream("/bundle.json");
Bundle bundle = ourFhirContext.newJsonParser().parseBundle(new InputStreamReader(bundleRes));
List<IResource> res = bundle.toListOfResources();
ourSystemDao.transaction(res);
Patient p1 = (Patient) res.get(0);
String id = p1.getId().getValue();
ourLog.info("ID: {}", id);
assertThat(id, not(containsString("5556918")));
assertThat(id, not(equalToIgnoringCase("")));
}
@Test
public void testTransactionUpdateMatchUrlWithOneMatch() {
String methodName = "testTransactionUpdateMatchUrlWithOneMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.UPDATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.UPDATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().toVersionless().toString(), not(containsString("test")));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateMatchUrlWithTwoMatch() {
String methodName = "testTransactionUpdateMatchUrlWithTwoMatch";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.UPDATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
try {
ourSystemDao.transaction(Arrays.asList((IResource) p, o));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("with match URL \"Patient"));
}
}
@Test
public void testTransactionUpdateMatchUrlWithZeroMatch() {
String methodName = "testTransactionUpdateMatchUrlWithZeroMatch";
Patient p = new Patient();
p.addName().addFamily("Hello");
IdDt id = ourPatientDao.create(p).getId();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId(id);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.UPDATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference(id);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.UPDATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(p.getId().toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateMatchUrlWithZeroMatchAndNotPreExisting() {
String methodName = "testTransactionUpdateMatchUrlWithZeroMatchAndNotPreExisting";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.UPDATE);
ResourceMetadataKeyEnum.LINK_SEARCH.put(p, "Patient?identifier=urn%3Asystem%7C" + methodName);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().toVersionless().toString(), containsString("test"));
assertThat(p.getId().toString(), endsWith("/_history/1"));
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(p.getId().toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateNoMatchUrl() {
String methodName = "testTransactionUpdateNoMatchUrl";
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.setId("Patient/" + methodName);
IdDt id = ourPatientDao.create(p).getId();
ourLog.info("Created patient, got it: {}", id);
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addFamily("Hello");
p.setId("Patient/" + methodName);
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.put(p, BundleEntryTransactionOperationEnum.UPDATE);
Observation o = new Observation();
o.getName().setText("Some Observation");
o.getSubject().setReference("Patient/" + methodName);
List<IResource> resp = ourSystemDao.transaction(Arrays.asList((IResource) p, o));
assertEquals(3, resp.size());
p = (Patient) resp.get(1);
assertEquals(BundleEntryTransactionOperationEnum.UPDATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(p));
assertThat(p.getId().toVersionless().toString(), containsString("test"));
assertEquals(id.toVersionless(), p.getId().toVersionless());
assertNotEquals(id, p.getId());
assertThat(p.getId().toString(), endsWith("/_history/2"));
o = (Observation) resp.get(2);
assertEquals(BundleEntryTransactionOperationEnum.CREATE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_OPERATION.get(o));
assertEquals(id.toVersionless(), o.getSubject().getReference());
}
@Test
public void testTransactionUpdateNoOperationSpecified() throws Exception {
List<IResource> res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.getId().setValue("testTransactionWithUpdateXXX01");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithUpdate01");
res.add(p1);
Observation p2 = new Observation();
p2.getId().setValue("testTransactionWithUpdateXXX02");
p2.getIdentifier().setSystem("system").setValue("testTransactionWithUpdate02");
p2.setSubject(new ResourceReferenceDt("Patient/testTransactionWithUpdateXXX01"));
res.add(p2);
ourSystemDao.transaction(res);
assertFalse(p1.getId().isEmpty());
assertFalse(p2.getId().isEmpty());
assertEquals("testTransactionWithUpdateXXX01", p1.getId().getIdPart());
assertEquals("testTransactionWithUpdateXXX02", p2.getId().getIdPart());
assertNotEquals("testTransactionWithUpdateXXX01", p1.getId().getVersionIdPart());
assertNotEquals("testTransactionWithUpdateXXX02", p2.getId().getVersionIdPart());
assertEquals(p1.getId().toUnqualified().toVersionless(), p2.getSubject().getReference());
IdDt p1id = p1.getId().toUnqualified().toVersionless();
IdDt p1idWithVer = p1.getId().toUnqualified();
IdDt p2id = p2.getId().toUnqualified().toVersionless();
IdDt p2idWithVer = p2.getId().toUnqualified();
/*
* Make some changes
*/
res = new ArrayList<IResource>();
p1 = new Patient();
p1.getId().setValue("testTransactionWithUpdateXXX01");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithUpdate01");
p1.addName().addFamily("Name1");
res.add(p1);
p2 = new Observation();
p2.getId().setValue("testTransactionWithUpdateXXX02");
p2.getIdentifier().setSystem("system").setValue("testTransactionWithUpdate02");
p2.setSubject(new ResourceReferenceDt("Patient/testTransactionWithUpdateXXX01"));
p2.addReferenceRange().setHigh(new QuantityDt(123L));
res.add(p2);
List<IResource> results = ourSystemDao.transaction(res);
assertEquals(p1id, results.get(1).getId().toUnqualified().toVersionless());
assertEquals(p2id, results.get(2).getId().toUnqualified().toVersionless());
assertNotEquals(p1idWithVer, results.get(1).getId().toUnqualified());
assertNotEquals(p2idWithVer, results.get(2).getId().toUnqualified());
}
/**
* Issue #55
*/
@Test
public void testTransactionWithCidIds() throws Exception {
List<IResource> res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.setId("cid:patient1");
p1.addIdentifier().setSystem("system").setValue("testTransactionWithCidIds01");
res.add(p1);
Observation o1 = new Observation();
o1.setId("cid:observation1");
o1.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds02");
o1.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
res.add(o1);
Observation o2 = new Observation();
o2.setId("cid:observation2");
o2.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds03");
o2.setSubject(new ResourceReferenceDt("Patient/cid:patient1"));
res.add(o2);
ourSystemDao.transaction(res);
assertTrue(p1.getId().getValue(), p1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o1.getId().getValue(), o1.getId().getIdPart().matches("^[0-9]+$"));
assertTrue(o2.getId().getValue(), o2.getId().getIdPart().matches("^[0-9]+$"));
assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
}
@Test
public void testTransactionWithDelete() throws Exception {
/*
* Create 3
*/
List<IResource> res;
res = new ArrayList<IResource>();
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p1);
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p2);
Patient p3 = new Patient();
p3.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
res.add(p3);
ourSystemDao.transaction(res);
/*
* Verify
*/
IBundleProvider results = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(3, results.size());
/*
* Now delete 2
*/
res = new ArrayList<IResource>();
List<IResource> existing = results.getResources(0, 3);
p1 = new Patient();
p1.setId(existing.get(0).getId());
ResourceMetadataKeyEnum.DELETED_AT.put(p1, InstantDt.withCurrentTime());
res.add(p1);
p2 = new Patient();
p2.setId(existing.get(1).getId());
ResourceMetadataKeyEnum.DELETED_AT.put(p2, InstantDt.withCurrentTime());
res.add(p2);
ourSystemDao.transaction(res);
/*
* Verify
*/
IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(1, results2.size());
List<IResource> existing2 = results2.getResources(0, 1);
assertEquals(existing2.get(0).getId(), existing.get(2).getId());
}
@AfterClass
public static void afterClass() {
ourCtx.close();
}
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() {
ourCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu1.xml", "fhir-jpabase-spring-test-config.xml");
ourFhirContext = ourCtx.getBean(FhirContext.class);
ourPatientDao = ourCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class);
ourObservationDao = ourCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class);
ourLocationDao = ourCtx.getBean("myLocationDaoDstu1", IFhirResourceDao.class);
ourSystemDao = ourCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
}
}

View File

@ -19,7 +19,6 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;

File diff suppressed because one or more lines are too long

View File

@ -1,74 +0,0 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
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.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.server.Constants;
public class GenericClientTestDev {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu2();
}
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testSearchByString() throws Exception {
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
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_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("james"))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString());
assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass());
}
}

View File

@ -0,0 +1,212 @@
package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
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.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class GenericClientTestDstu2 {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu2();
}
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testSearchByString() throws Exception {
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
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_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("james"))
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString());
assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass());
}
@Test
public void testDeleteConditional() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
// when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
client.delete().resourceById(new IdDt("Patient/123")).execute();
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
idx++;
client.delete().resourceConditionalByUrl("Patient?name=foo").execute();
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getURI().toString());
idx++;
client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute();
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getURI().toString());
idx++;
}
@Test
public void testCreateConditional() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
Patient p = new Patient();
p.addName().addFamily("FOOFAMILY");
client.create().resource(p).conditionalByUrl("Patient?name=foo").execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_IF_NONE_EXIST).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_IF_NONE_EXIST).getValue());
assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod());
idx++;
}
@Test
public void testUpdateConditional() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
Patient p = new Patient();
p.addName().addFamily("FOOFAMILY");
client.update().resource(p).conditionalByUrl("Patient?name=foo").execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getURI().toString());
idx++;
client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo", capt.getAllValues().get(idx).getURI().toString());
idx++;
client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo&address=AAA%5C%7CBBB", capt.getAllValues().get(idx).getURI().toString());
idx++;
client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute();
assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, idx), containsString("<family value=\"FOOFAMILY\"/>"));
assertEquals("PUT", capt.getAllValues().get(idx).getRequestLine().getMethod());
assertEquals("http://example.com/fhir/Patient?name=foo&address=AAA%5C%7CBBB", capt.getAllValues().get(idx).getURI().toString());
idx++;
}
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(count)).getEntity().getContent(), "UTF-8");
return body;
}
}

View File

@ -0,0 +1,133 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPut;
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.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class UpdateConditionalTest {
private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalTest.class);
private static int ourPort;
private static Server ourServer;
@Test
public void testUpdate() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001");
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("OODETAILS", oo.getIssueFirstRep().getDetails());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
private static String ourLastConditionalUrl;
@Before
public void before() {
ourLastConditionalUrl=null;
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
@Update()
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
IdDt id = theId.withVersion(thePatient.getIdentifierFirstRep().getValue());
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("OODETAILS");
if (theId.getValueAsString().contains("CREATE")) {
return new MethodOutcome(id,oo, true);
}
return new MethodOutcome(id,oo);
}
}
}