Improve nexted search/read handing in transactions in JPA server
This commit is contained in:
parent
9b5598e2ab
commit
ae416dcd62
|
@ -15,6 +15,11 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
|
|
|
@ -67,7 +67,7 @@ import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
|||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Object> {
|
||||
public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Object> {
|
||||
protected static final Set<String> ALLOWED_PARAMS;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReturningMethodBinding.class);
|
||||
|
||||
|
@ -102,8 +102,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
Class<?> collectionType = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
||||
if (collectionType != null) {
|
||||
if (!Object.class.equals(collectionType) && !IBaseResource.class.isAssignableFrom(collectionType)) {
|
||||
throw new ConfigurationException("Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: "
|
||||
+ collectionType);
|
||||
throw new ConfigurationException(
|
||||
"Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: " + collectionType);
|
||||
}
|
||||
}
|
||||
myResourceListCollectionType = collectionType;
|
||||
|
@ -121,8 +121,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
} else if (MethodOutcome.class.isAssignableFrom(methodReturnType)) {
|
||||
myMethodReturnType = MethodReturnTypeEnum.METHOD_OUTCOME;
|
||||
} else {
|
||||
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: "
|
||||
+ theMethod.getDeclaringClass().getCanonicalName());
|
||||
throw new ConfigurationException(
|
||||
"Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
||||
if (theReturnResourceType != null) {
|
||||
|
@ -233,29 +233,52 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
throw new IllegalStateException("Should not get here!");
|
||||
}
|
||||
|
||||
public abstract Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
|
||||
// Pretty print
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
|
||||
// Narrative mode
|
||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest(), theServer.getDefaultResponseEncoding());
|
||||
|
||||
// Is this request coming from a browser
|
||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||
boolean requestIsBrowser = false;
|
||||
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
||||
requestIsBrowser = true;
|
||||
}
|
||||
|
||||
byte[] requestContents = loadRequestContents(theRequest);
|
||||
|
||||
|
||||
final ResourceOrDstu1Bundle responseObject = invokeServer(theServer, theRequest, requestContents);
|
||||
|
||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
if (responseObject.getResource() != null) {
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getResource(), theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(),
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
} else {
|
||||
// Is this request coming from a browser
|
||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||
boolean requestIsBrowser = false;
|
||||
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
||||
requestIsBrowser = true;
|
||||
}
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle(), theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, responseObject.getDstu1Bundle(), theRequest.getFhirServerBase(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser,
|
||||
theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceOrDstu1Bundle invokeServer(RestfulServer theServer, RequestDetails theRequest, byte[] requestContents) {
|
||||
// Method params
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
|
@ -267,9 +290,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
Object resultObj = invokeServer(theServer, theRequest, params);
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
|
||||
final ResourceOrDstu1Bundle responseObject;
|
||||
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE: {
|
||||
|
||||
|
@ -284,7 +308,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
b.append('/');
|
||||
b.append(theRequest.getRequestPath());
|
||||
}
|
||||
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
|
||||
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
boolean first = true;
|
||||
Map<String, String[]> parameters = theRequest.getParameters();
|
||||
|
@ -323,17 +347,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
bundleFactory.initializeWithBundleResource(resource);
|
||||
bundleFactory.addRootPropertiesToBundle(null, theRequest.getFhirServerBase(), linkSelf, count, getResponseBundleType(), lastUpdated);
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
responseObject = new ResourceOrDstu1Bundle(resource);
|
||||
break;
|
||||
} else {
|
||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||
|
@ -344,32 +358,18 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
|
||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest(), theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) ? responseEncoding : null;
|
||||
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, 0, count, null, getResponseBundleType(),
|
||||
includes);
|
||||
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, 0, count, null, getResponseBundleType(), includes);
|
||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||
if (bundle != null) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, theRequest.getFhirServerBase(), summaryMode, respondGzip, requestIsBrowser, theRequest);
|
||||
responseObject = new ResourceOrDstu1Bundle(bundle);
|
||||
} else {
|
||||
IBaseResource resBundle = bundleFactory.getResourceBundle();
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resBundle, prettyPrint, summaryMode,
|
||||
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), isAddContentLocationHeader(), theRequest);
|
||||
responseObject = new ResourceOrDstu1Bundle(resBundle);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -384,22 +384,17 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
|
||||
IBaseResource resource = result.getResources(0, 1).get(0);
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
responseObject = new ResourceOrDstu1Bundle(resource);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException(); // should not happen
|
||||
}
|
||||
return responseObject;
|
||||
}
|
||||
|
||||
public abstract Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||
|
||||
/**
|
||||
* Should the response include a Content-Location header. Search method bunding (and any others?) may override this to disable the content-location, since it doesn't make sense
|
||||
*/
|
||||
|
@ -412,7 +407,32 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
|
||||
public enum MethodReturnTypeEnum {
|
||||
BUNDLE, BUNDLE_PROVIDER, BUNDLE_RESOURCE, LIST_OF_RESOURCES, RESOURCE, METHOD_OUTCOME
|
||||
BUNDLE, BUNDLE_PROVIDER, BUNDLE_RESOURCE, LIST_OF_RESOURCES, METHOD_OUTCOME, RESOURCE
|
||||
}
|
||||
|
||||
public static class ResourceOrDstu1Bundle {
|
||||
|
||||
private final Bundle myDstu1Bundle;
|
||||
private final IBaseResource myResource;
|
||||
|
||||
public ResourceOrDstu1Bundle(Bundle theBundle) {
|
||||
myDstu1Bundle = theBundle;
|
||||
myResource = null;
|
||||
}
|
||||
|
||||
public ResourceOrDstu1Bundle(IBaseResource theResource) {
|
||||
myResource = theResource;
|
||||
myDstu1Bundle = null;
|
||||
}
|
||||
|
||||
public Bundle getDstu1Bundle() {
|
||||
return myDstu1Bundle;
|
||||
}
|
||||
|
||||
public IBaseResource getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum ReturnTypeEnum {
|
||||
|
|
|
@ -214,7 +214,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||
IBundleProvider retVal = toResourceList(response);
|
||||
|
||||
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
String ifNoneMatch = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
||||
String ifNoneMatch = theRequest.getFirstHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
||||
if (retVal.size() == 1 && StringUtils.isNotBlank(ifNoneMatch)) {
|
||||
List<IBaseResource> responseResources = retVal.getResources(0, 1);
|
||||
IBaseResource responseResource = responseResources.get(0);
|
||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
|||
|
||||
public class RequestDetails {
|
||||
|
||||
private Map<String, ArrayList<String>> myHeaders = new HashMap<String, ArrayList<String>>();
|
||||
private String myCompartmentName;
|
||||
private String myCompleteUrl;
|
||||
private String myFhirServerBase;
|
||||
|
@ -230,4 +231,22 @@ public class RequestDetails {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public void addHeader(String theName, String theValue) {
|
||||
String lowerCase = theName.toLowerCase();
|
||||
ArrayList<String> list = myHeaders.get(lowerCase);
|
||||
if (list == null) {
|
||||
list = new ArrayList<String>();
|
||||
myHeaders.put(lowerCase, list);
|
||||
}
|
||||
list.add(theValue);
|
||||
}
|
||||
|
||||
public String getFirstHeader(String theName) {
|
||||
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
|
||||
if (list == null || list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,9 +52,6 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -477,14 +478,14 @@ public class RestfulServer extends HttpServlet {
|
|||
return;
|
||||
}
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
if (count == null) {
|
||||
count = getPagingProvider().getDefaultPageSize();
|
||||
} else if (count > getPagingProvider().getMaximumPageSize()) {
|
||||
count = getPagingProvider().getMaximumPageSize();
|
||||
}
|
||||
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest.getServletRequest(), Constants.PARAM_PAGINGOFFSET);
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
||||
if (offsetI == null || offsetI < 0) {
|
||||
offsetI = 0;
|
||||
}
|
||||
|
@ -545,6 +546,12 @@ public class RestfulServer extends HttpServlet {
|
|||
requestDetails.setRequestType(theRequestType);
|
||||
requestDetails.setServletRequest(theRequest);
|
||||
requestDetails.setServletResponse(theResponse);
|
||||
for (Enumeration<String> iter = theRequest.getHeaderNames(); iter.hasMoreElements(); ) {
|
||||
String nextName = iter.nextElement();
|
||||
for (Enumeration<String> valueIter = theRequest.getHeaders(nextName); valueIter.hasMoreElements();) {
|
||||
requestDetails.addHeader(nextName, valueIter.nextElement());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
|
@ -556,7 +563,6 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
String resourceName = null;
|
||||
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
|
||||
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
|
||||
StringBuffer requestUrl = theRequest.getRequestURL();
|
||||
|
@ -572,11 +578,9 @@ public class RestfulServer extends HttpServlet {
|
|||
ourLog.trace("Context Path: {}", servletContextPath);
|
||||
}
|
||||
|
||||
IdDt id = null;
|
||||
String operation = null;
|
||||
String compartment = null;
|
||||
|
||||
|
||||
String requestPath = getRequestPath(requestFullPath, servletContextPath, servletPath);
|
||||
|
||||
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
|
||||
requestPath = requestPath.substring(1);
|
||||
}
|
||||
|
@ -588,84 +592,16 @@ public class RestfulServer extends HttpServlet {
|
|||
Map<String, String[]> params = new HashMap<String, String[]>(theRequest.getParameterMap());
|
||||
requestDetails.setParameters(params);
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(requestPath, "/");
|
||||
if (tok.hasMoreTokens()) {
|
||||
resourceName = tok.nextToken();
|
||||
if (partIsOperation(resourceName)) {
|
||||
operation = resourceName;
|
||||
resourceName = null;
|
||||
}
|
||||
}
|
||||
requestDetails.setResourceName(resourceName);
|
||||
|
||||
ResourceBinding resourceBinding = null;
|
||||
BaseMethodBinding<?> resourceMethod = null;
|
||||
if (Constants.URL_TOKEN_METADATA.equals(resourceName) || theRequestType == RequestTypeEnum.OPTIONS) {
|
||||
resourceMethod = myServerConformanceMethod;
|
||||
} else if (resourceName == null) {
|
||||
resourceBinding = myServerBinding;
|
||||
} else {
|
||||
resourceBinding = myResourceNameToBinding.get(resourceName);
|
||||
if (resourceBinding == null) {
|
||||
throw new InvalidRequestException("Unknown resource type '" + resourceName + "' - Server knows how to handle: " + myResourceNameToBinding.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (partIsOperation(nextString)) {
|
||||
operation = nextString;
|
||||
} else {
|
||||
id = new IdDt(resourceName, UrlUtil.unescape(nextString));
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (nextString.equals(Constants.PARAM_HISTORY)) {
|
||||
if (tok.hasMoreTokens()) {
|
||||
String versionString = tok.nextToken();
|
||||
if (id == null) {
|
||||
throw new InvalidRequestException("Don't know how to handle request path: " + requestPath);
|
||||
}
|
||||
id = new IdDt(resourceName, id.getIdPart(), UrlUtil.unescape(versionString));
|
||||
} else {
|
||||
operation = Constants.PARAM_HISTORY;
|
||||
}
|
||||
} else if (partIsOperation(nextString)) {
|
||||
if (operation != null) {
|
||||
throw new InvalidRequestException("URL Path contains two operations: " + requestPath);
|
||||
}
|
||||
operation = nextString;
|
||||
} else {
|
||||
compartment = nextString;
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary is for things like ..../_tags/_delete
|
||||
String secondaryOperation = null;
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (operation == null) {
|
||||
operation = nextString;
|
||||
} else if (secondaryOperation == null) {
|
||||
secondaryOperation = nextString;
|
||||
} else {
|
||||
throw new InvalidRequestException("URL path has unexpected token '" + nextString + "' at the end: " + requestPath);
|
||||
}
|
||||
}
|
||||
IdDt id;
|
||||
populateRequestDetailsFromRequestPath(requestDetails, requestPath);
|
||||
|
||||
if (theRequestType == RequestTypeEnum.PUT) {
|
||||
String contentLocation = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
if (contentLocation != null) {
|
||||
id = new IdDt(contentLocation);
|
||||
requestDetails.setId(id);
|
||||
}
|
||||
}
|
||||
requestDetails.setId(id);
|
||||
requestDetails.setOperation(operation);
|
||||
requestDetails.setSecondaryOperation(secondaryOperation);
|
||||
requestDetails.setCompartmentName(compartment);
|
||||
|
||||
String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING);
|
||||
boolean respondGzip = false;
|
||||
|
@ -696,18 +632,7 @@ public class RestfulServer extends HttpServlet {
|
|||
return;
|
||||
}
|
||||
|
||||
if (resourceMethod == null) {
|
||||
if (resourceBinding != null) {
|
||||
resourceMethod = resourceBinding.getMethod(requestDetails);
|
||||
}
|
||||
}
|
||||
if (resourceMethod == null) {
|
||||
if (isBlank(requestPath)) {
|
||||
throw new InvalidRequestException(myFhirContext.getLocalizer().getMessage(RestfulServer.class, "rootRequest"));
|
||||
} else {
|
||||
throw new InvalidRequestException(myFhirContext.getLocalizer().getMessage(RestfulServer.class, "unknownMethod", theRequestType.name(), requestPath, params.keySet()));
|
||||
}
|
||||
}
|
||||
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
|
||||
|
||||
requestDetails.setRestOperationType(resourceMethod.getRestOperationType());
|
||||
|
||||
|
@ -806,6 +731,105 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
public BaseMethodBinding<?> determineResourceMethod(RequestDetails requestDetails, String requestPath) {
|
||||
RequestTypeEnum requestType = requestDetails.getRequestType();
|
||||
|
||||
ResourceBinding resourceBinding = null;
|
||||
BaseMethodBinding<?> resourceMethod = null;
|
||||
String resourceName = requestDetails.getResourceName();
|
||||
if (Constants.URL_TOKEN_METADATA.equals(resourceName) || requestType == RequestTypeEnum.OPTIONS) {
|
||||
resourceMethod = myServerConformanceMethod;
|
||||
} else if (resourceName == null) {
|
||||
resourceBinding = myServerBinding;
|
||||
} else {
|
||||
resourceBinding = myResourceNameToBinding.get(resourceName);
|
||||
if (resourceBinding == null) {
|
||||
throw new InvalidRequestException("Unknown resource type '" + resourceName + "' - Server knows how to handle: " + myResourceNameToBinding.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceMethod == null) {
|
||||
if (resourceBinding != null) {
|
||||
resourceMethod = resourceBinding.getMethod(requestDetails);
|
||||
}
|
||||
}
|
||||
if (resourceMethod == null) {
|
||||
if (isBlank(requestPath)) {
|
||||
throw new InvalidRequestException(myFhirContext.getLocalizer().getMessage(RestfulServer.class, "rootRequest"));
|
||||
} else {
|
||||
throw new InvalidRequestException(myFhirContext.getLocalizer().getMessage(RestfulServer.class, "unknownMethod", requestType.name(), requestPath, requestDetails.getParameters().keySet()));
|
||||
}
|
||||
}
|
||||
return resourceMethod;
|
||||
}
|
||||
|
||||
public void populateRequestDetailsFromRequestPath(RequestDetails theRequestDetails, String theRequestPath) {
|
||||
StringTokenizer tok = new StringTokenizer(theRequestPath, "/");
|
||||
String resourceName = null;
|
||||
|
||||
IdDt id = null;
|
||||
String operation = null;
|
||||
String compartment = null;
|
||||
if (tok.hasMoreTokens()) {
|
||||
resourceName = tok.nextToken();
|
||||
if (partIsOperation(resourceName)) {
|
||||
operation = resourceName;
|
||||
resourceName = null;
|
||||
}
|
||||
}
|
||||
theRequestDetails.setResourceName(resourceName);
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (partIsOperation(nextString)) {
|
||||
operation = nextString;
|
||||
} else {
|
||||
id = new IdDt(resourceName, UrlUtil.unescape(nextString));
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (nextString.equals(Constants.PARAM_HISTORY)) {
|
||||
if (tok.hasMoreTokens()) {
|
||||
String versionString = tok.nextToken();
|
||||
if (id == null) {
|
||||
throw new InvalidRequestException("Don't know how to handle request path: " + theRequestPath);
|
||||
}
|
||||
id = new IdDt(resourceName, id.getIdPart(), UrlUtil.unescape(versionString));
|
||||
} else {
|
||||
operation = Constants.PARAM_HISTORY;
|
||||
}
|
||||
} else if (partIsOperation(nextString)) {
|
||||
if (operation != null) {
|
||||
throw new InvalidRequestException("URL Path contains two operations: " + theRequestPath);
|
||||
}
|
||||
operation = nextString;
|
||||
} else {
|
||||
compartment = nextString;
|
||||
}
|
||||
}
|
||||
|
||||
// Secondary is for things like ..../_tags/_delete
|
||||
String secondaryOperation = null;
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (operation == null) {
|
||||
operation = nextString;
|
||||
} else if (secondaryOperation == null) {
|
||||
secondaryOperation = nextString;
|
||||
} else {
|
||||
throw new InvalidRequestException("URL path has unexpected token '" + nextString + "' at the end: " + theRequestPath);
|
||||
}
|
||||
}
|
||||
|
||||
theRequestDetails.setId(id);
|
||||
theRequestDetails.setOperation(operation);
|
||||
theRequestDetails.setSecondaryOperation(secondaryOperation);
|
||||
theRequestDetails.setCompartmentName(compartment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
||||
* called immediately before beginning initialization of the restful server's internal init.
|
||||
|
|
|
@ -394,8 +394,9 @@ public class RestfulServerUtils {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public static Integer extractCountParameter(HttpServletRequest theRequest) {
|
||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
||||
public static Integer extractCountParameter(RequestDetails theRequest) {
|
||||
String paramName = Constants.PARAM_COUNT;
|
||||
return tryToExtractNamedParameter(theRequest, paramName);
|
||||
}
|
||||
|
||||
public static IParser getNewParser(FhirContext theContext, RequestDetails theRequestDetails) {
|
||||
|
@ -674,18 +675,18 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
Integer count = null;
|
||||
if (isNotBlank(countString)) {
|
||||
try {
|
||||
count = Integer.parseInt(countString);
|
||||
} catch (NumberFormatException e) {
|
||||
ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
// static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
// String countString = theRequest.getParameter(name);
|
||||
// Integer count = null;
|
||||
// if (isNotBlank(countString)) {
|
||||
// try {
|
||||
// count = Integer.parseInt(countString);
|
||||
// } catch (NumberFormatException e) {
|
||||
// ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
// }
|
||||
// }
|
||||
// return count;
|
||||
// }
|
||||
|
||||
public static void validateResourceListNotNull(List<? extends IBaseResource> theResourceList) {
|
||||
if (theResourceList == null) {
|
||||
|
@ -701,4 +702,17 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static Integer tryToExtractNamedParameter(RequestDetails theRequest, String theParamName) {
|
||||
String[] retVal = theRequest.getParameters().get(theParamName);
|
||||
if (retVal == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(retVal[0]);
|
||||
} catch (NumberFormatException e) {
|
||||
ourLog.debug("Failed to parse {} value '{}': {}", new Object[] {theParamName, retVal[0], e});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ public class UrlUtil {
|
|||
char nextChar = url.charAt(idx);
|
||||
boolean atEnd = (idx + 1) == url.length();
|
||||
if (nextChar == '?' || nextChar == '/' || atEnd) {
|
||||
int endIdx = atEnd ? idx + 1 : idx;
|
||||
int endIdx = (atEnd && nextChar != '?') ? idx + 1 : idx;
|
||||
String nextSubstring = url.substring(nextStart, endIdx);
|
||||
if (retVal.getResourceType() == null) {
|
||||
retVal.setResourceType(nextSubstring);
|
||||
|
|
|
@ -802,22 +802,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
public static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
List<NameValuePair> parameters;
|
||||
String matchUrl = theMatchUrl;
|
||||
int questionMarkIndex = matchUrl.indexOf('?');
|
||||
if (questionMarkIndex != -1) {
|
||||
matchUrl = matchUrl.substring(questionMarkIndex + 1);
|
||||
}
|
||||
matchUrl = matchUrl.replace("|", "%7C");
|
||||
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||
if (matchUrl.contains(" ")) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
|
||||
}
|
||||
|
||||
parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
|
||||
List<NameValuePair> parameters = translateMatchUrl(theMatchUrl);
|
||||
|
||||
ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create();
|
||||
for (NameValuePair next : parameters) {
|
||||
|
@ -891,6 +876,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return paramMap;
|
||||
}
|
||||
|
||||
protected static List<NameValuePair> translateMatchUrl(String theMatchUrl) {
|
||||
List<NameValuePair> parameters;
|
||||
String matchUrl = theMatchUrl;
|
||||
int questionMarkIndex = matchUrl.indexOf('?');
|
||||
if (questionMarkIndex != -1) {
|
||||
matchUrl = matchUrl.substring(questionMarkIndex + 1);
|
||||
}
|
||||
matchUrl = matchUrl.replace("|", "%7C");
|
||||
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||
if (matchUrl.contains(" ")) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
|
||||
}
|
||||
|
||||
parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDaoListener(IDaoListener theListener) {
|
||||
Validate.notNull(theListener, "theListener");
|
||||
|
|
|
@ -22,11 +22,10 @@ package ca.uhn.fhir.jpa.dao;
|
|||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hamcrest.Matchers.emptyCollectionOf;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -34,6 +33,7 @@ import java.util.Set;
|
|||
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
@ -44,10 +44,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
|
@ -61,16 +62,19 @@ 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.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.BaseResourceReturningMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.BaseResourceReturningMethodBinding.ResourceOrDstu1Bundle;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
@ -103,8 +107,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
resp.addEntry().setResource(ooResp);
|
||||
|
||||
/*
|
||||
* For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it
|
||||
* doesn't prevent others
|
||||
* For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it doesn't prevent others
|
||||
*/
|
||||
|
||||
for (final Entry nextRequestEntry : theRequest.getEntry()) {
|
||||
|
@ -129,8 +132,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
Entry subResponseEntry = nextResponseBundle.getEntry().get(0);
|
||||
resp.addEntry(subResponseEntry);
|
||||
/*
|
||||
* If the individual entry didn't have a resource in its response, bring the sub-transaction's
|
||||
* OperationOutcome across so the client can see it
|
||||
* If the individual entry didn't have a resource in its response, bring the sub-transaction's OperationOutcome across so the client can see it
|
||||
*/
|
||||
if (subResponseEntry.getResource() == null) {
|
||||
subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
|
||||
|
@ -229,7 +231,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
if (res != null) {
|
||||
|
||||
nextResourceId = res.getId();
|
||||
|
||||
|
||||
if (nextResourceId.hasIdPart() == false) {
|
||||
if (isNotBlank(nextEntry.getFullUrl())) {
|
||||
nextResourceId = new IdDt(nextEntry.getFullUrl());
|
||||
|
@ -239,7 +241,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
if (nextResourceId.hasIdPart() && nextResourceId.getIdPart().matches("[a-zA-Z]+\\:.*") && !isPlaceholder(nextResourceId)) {
|
||||
throw new InvalidRequestException("Invalid placeholder ID found: " + nextResourceId.getIdPart() + " - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'");
|
||||
}
|
||||
|
||||
|
||||
if (nextResourceId.hasIdPart() && !nextResourceId.hasResourceType() && !isPlaceholder(nextResourceId)) {
|
||||
nextResourceId = new IdDt(toResourceName(res.getClass()), nextResourceId.getIdPart());
|
||||
res.setId(nextResourceId);
|
||||
|
@ -319,72 +321,67 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
}
|
||||
case GET: {
|
||||
// SEARCH/READ/VREAD
|
||||
RequestDetails requestDetails = new RequestDetails();
|
||||
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
|
||||
requestDetails.setRequestType(RequestTypeEnum.GET);
|
||||
requestDetails.setServer(theRequestDetails.getServer());
|
||||
|
||||
String url = extractTransactionUrlOrThrowException(nextEntry, verb);
|
||||
UrlParts parts = UrlUtil.parseUrl(url);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao dao = toDao(parts, verb.getCode(), url);
|
||||
|
||||
String ifNoneMatch = nextEntry.getRequest().getIfNoneMatch();
|
||||
if (isNotBlank(ifNoneMatch)) {
|
||||
ifNoneMatch = MethodUtil.parseETagValue(ifNoneMatch);
|
||||
|
||||
int qIndex = url.indexOf('?');
|
||||
ArrayListMultimap<String, String> paramValues = ArrayListMultimap.create();
|
||||
requestDetails.setParameters(new HashMap<String, String[]>());
|
||||
if (qIndex != -1) {
|
||||
String params = url.substring(qIndex);
|
||||
List<NameValuePair> parameters = translateMatchUrl(params);
|
||||
for (NameValuePair next : parameters) {
|
||||
paramValues.put(next.getName(), next.getValue());
|
||||
}
|
||||
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
|
||||
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
|
||||
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue);
|
||||
}
|
||||
url = url.substring(0, qIndex);
|
||||
}
|
||||
|
||||
if (parts.getResourceId() != null && parts.getParams() == null) {
|
||||
/*
|
||||
* GET is a read
|
||||
*/
|
||||
IResource found;
|
||||
boolean notChanged = false;
|
||||
if (parts.getVersionId() != null) {
|
||||
if (isNotBlank(ifNoneMatch)) {
|
||||
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
|
||||
}
|
||||
found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
|
||||
} else {
|
||||
found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
|
||||
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
|
||||
notChanged = true;
|
||||
requestDetails.setRequestPath(url);
|
||||
requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase());
|
||||
|
||||
theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url);
|
||||
BaseMethodBinding<?> method = theRequestDetails.getServer().determineResourceMethod(requestDetails, url);
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Unable to handle GET " + url);
|
||||
}
|
||||
|
||||
if (isNotBlank(nextEntry.getRequest().getIfMatch())) {
|
||||
requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextEntry.getRequest().getIfMatch());
|
||||
}
|
||||
if (isNotBlank(nextEntry.getRequest().getIfNoneExist())) {
|
||||
requestDetails.addHeader(Constants.HEADER_IF_NONE_EXIST, nextEntry.getRequest().getIfNoneExist());
|
||||
}
|
||||
if (isNotBlank(nextEntry.getRequest().getIfNoneMatch())) {
|
||||
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextEntry.getRequest().getIfNoneMatch());
|
||||
}
|
||||
|
||||
if (method instanceof BaseResourceReturningMethodBinding) {
|
||||
try {
|
||||
ResourceOrDstu1Bundle responseData = ((BaseResourceReturningMethodBinding) method).invokeServer(theRequestDetails.getServer(), requestDetails, new byte[0]);
|
||||
Entry newEntry = response.addEntry();
|
||||
IBaseResource resource = responseData.getResource();
|
||||
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
|
||||
resource = filterNestedBundle(requestDetails, resource);
|
||||
}
|
||||
newEntry.setResource((IResource) resource);
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
} catch (NotModifiedException e) {
|
||||
Entry newEntry = response.addEntry();
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
||||
}
|
||||
Entry entry = response.addEntry();
|
||||
if (notChanged == false) {
|
||||
entry.setResource(found);
|
||||
}
|
||||
EntryResponse resp = entry.getResponse();
|
||||
resp.setLocation(found.getId().toUnqualified().getValue());
|
||||
resp.setEtag(found.getId().getVersionIdPart());
|
||||
if (!notChanged) {
|
||||
resp.setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
} else {
|
||||
resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
||||
}
|
||||
} else if (parts.getParams() != null) {
|
||||
/*
|
||||
* GET is a search
|
||||
*/
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(dao.getResourceType());
|
||||
SearchParameterMap params = translateMatchUrl(url, def);
|
||||
IBundleProvider bundle = dao.search(params);
|
||||
|
||||
Integer count = params.getCount();
|
||||
if (count == null) {
|
||||
count = bundle.preferredPageSize();
|
||||
}
|
||||
|
||||
IVersionSpecificBundleFactory bundleFactory = getContext().newBundleFactory();
|
||||
|
||||
Set<Include> includes = new HashSet<Include>();
|
||||
EncodingEnum linkEncoding = null;
|
||||
bundleFactory.initializeBundleFromBundleProvider(theRequestDetails.getServer(), bundle, linkEncoding, theRequestDetails.getFhirServerBase(), url, false, 0, count, null, ca.uhn.fhir.model.valueset.BundleTypeEnum.SEARCHSET, includes);
|
||||
|
||||
Entry newEntry = response.addEntry();
|
||||
newEntry.setResource((IResource) bundleFactory.getResourceBundle());
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unable to handle GET " + url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FhirTerser terser = getContext().newTerser();
|
||||
|
@ -428,7 +425,8 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass());
|
||||
Set<Long> val = resourceDao.processMatchUrl(matchUrl);
|
||||
if (val.size() > 1) {
|
||||
throw new InvalidRequestException("Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
||||
throw new InvalidRequestException(
|
||||
"Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -454,6 +452,20 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called for nested bundles (e.g. if we received a transaction with an entry that
|
||||
* was a GET search, this method is called on the bundle for the search result, that will be placed in the
|
||||
* outer bundle). This method applies the _summary and _content parameters to the output of
|
||||
* that bundle.
|
||||
*
|
||||
* TODO: This isn't the most efficient way of doing this.. hopefully we can come up with something better in the future.
|
||||
*/
|
||||
private IBaseResource filterNestedBundle(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||
IParser p = getContext().newJsonParser();
|
||||
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
|
||||
return p.parseResource(theResource.getClass(), p.encodeResourceToString(theResource));
|
||||
}
|
||||
|
||||
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
|
||||
RuntimeResourceDefinition resType;
|
||||
try {
|
||||
|
@ -471,10 +483,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (theParts.getResourceId() == null && theParts.getParams() == null) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
// if (theParts.getResourceId() == null && theParts.getParams() == null) {
|
||||
// String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||
// throw new InvalidRequestException(msg);
|
||||
// }
|
||||
|
||||
return dao;
|
||||
}
|
||||
|
@ -487,15 +499,15 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, Entry newEntry, String theResourceType, IResource theRes) {
|
||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome,
|
||||
Entry newEntry, String theResourceType, IResource theRes) {
|
||||
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
|
||||
IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
|
||||
if (newId.equals(resourceId) == false) {
|
||||
idSubstitutions.put(resourceId, newId);
|
||||
if (isPlaceholder(resourceId)) {
|
||||
/*
|
||||
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified
|
||||
* kind too just to be lenient.
|
||||
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified kind too just to be lenient.
|
||||
*/
|
||||
idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class BaseJpaProvider {
|
|||
|
||||
Enumeration<String> forwardedFors = theRequest.getHeaders("x-forwarded-for");
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (Enumeration<String> enums = forwardedFors; enums.hasMoreElements();) {
|
||||
for (Enumeration<String> enums = forwardedFors; enums != null && enums.hasMoreElements();) {
|
||||
if (b.length() > 0) {
|
||||
b.append(" / ");
|
||||
}
|
||||
|
|
|
@ -22,8 +22,14 @@ import static org.mockito.Mockito.when;
|
|||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.Before;
|
||||
|
@ -37,6 +43,7 @@ import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
@ -63,6 +70,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
@ -74,6 +82,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
|
||||
private RequestDetails myRequestDetails;
|
||||
private RestfulServer myServer;
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle6() throws Exception {
|
||||
|
@ -310,10 +319,25 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void before() {
|
||||
public void before() throws ServletException {
|
||||
myRequestDetails = mock(RequestDetails.class);
|
||||
when(myRequestDetails.getServer()).thenReturn(new RestfulServer());
|
||||
|
||||
if (myServer == null) {
|
||||
myServer = new RestfulServer(myFhirCtx);
|
||||
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(myPatientDao);
|
||||
myServer.setResourceProviders(patientRp);
|
||||
myServer.init(mock(ServletConfig.class));
|
||||
}
|
||||
|
||||
when(myRequestDetails.getServer()).thenReturn(myServer);
|
||||
HttpServletRequest servletRequest = mock(HttpServletRequest.class);
|
||||
when(myRequestDetails.getServletRequest()).thenReturn(servletRequest);
|
||||
when(servletRequest.getHeaderNames()).thenReturn(mock(Enumeration.class));
|
||||
when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("/Patient"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1029,8 +1053,8 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertEquals("200 OK", nextEntry.getResponse().getStatus());
|
||||
|
||||
nextEntry = resp.getEntry().get(2);
|
||||
assertNull(nextEntry.getResource());
|
||||
assertEquals("304 Not Modified", nextEntry.getResponse().getStatus());
|
||||
assertNull(nextEntry.getResource());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -2,7 +2,12 @@ package ca.uhn.fhir.jpa.provider;
|
|||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -16,29 +21,22 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.TestDstu1Config;
|
||||
import ca.uhn.fhir.jpa.config.TestDstu2Config;
|
||||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.BaseJpaDstu2Test;
|
||||
import ca.uhn.fhir.jpa.rp.dstu2.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu2.OrganizationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
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.Questionnaire;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationDefinition;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
@ -46,18 +44,95 @@ import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
|||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import net.sf.saxon.lib.OutputURIResolver;
|
||||
|
||||
public class SystemProviderDstu2Test extends BaseJpaTest {
|
||||
public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
|
||||
|
||||
private static RestfulServer myRestServer;
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static CloseableHttpClient ourHttpClient;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderDstu2Test.class);
|
||||
private static Server ourServer;
|
||||
private static AnnotationConfigApplicationContext ourAppCtx;
|
||||
private static FhirContext ourCtx;
|
||||
private static IGenericClient ourClient;
|
||||
private static String ourServerBase;
|
||||
private static CloseableHttpClient ourHttpClient;
|
||||
private static RestfulServer restServer;
|
||||
|
||||
@Before
|
||||
public void beforeStartServer() throws Exception {
|
||||
if (myRestServer == null) {
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(myPatientDao);
|
||||
|
||||
QuestionnaireResourceProviderDstu2 questionnaireRp = new QuestionnaireResourceProviderDstu2();
|
||||
questionnaireRp.setDao(myQuestionnaireDao);
|
||||
|
||||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(myObservationDao);
|
||||
|
||||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(myOrganizationDao);
|
||||
|
||||
RestfulServer restServer = new RestfulServer(ourCtx);
|
||||
restServer.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10));
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
restServer.setPlainProviders(mySystemProvider);
|
||||
|
||||
int myPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(myPort);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
restServer.setFhirContext(ourCtx);
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourHttpClient = builder.build();
|
||||
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||
ourClient.setLogRequestAndResponse(true);
|
||||
myRestServer = restServer;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingReturnsCorrectFormatInPagingLink() throws Exception {
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
myRestServer.setPagingProvider(new FifoMemoryPagingProvider(1).setDefaultPageSize(10));
|
||||
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
|
||||
myRestServer.registerInterceptor(interceptor);
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("Name" + i);
|
||||
ourClient.create().resource(p).execute();
|
||||
}
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
|
||||
get.addHeader("Accept", "application/xml, text/html");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
try {
|
||||
String response = IOUtils.toString(http.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
assertThat(response, not(containsString("_format")));
|
||||
assertEquals(200, http.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
http.close();
|
||||
}
|
||||
|
||||
myRestServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingType() throws Exception {
|
||||
|
@ -71,74 +146,9 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingReturnsCorrectFormatInPagingLink() throws Exception {
|
||||
restServer.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
restServer.setPagingProvider(new FifoMemoryPagingProvider(1).setDefaultPageSize(10));
|
||||
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
|
||||
restServer.registerInterceptor(interceptor);
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("Name" + i);
|
||||
ourClient.create().resource(p).execute();
|
||||
}
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
|
||||
get.addHeader("Accept", "application/xml, text/html");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
try {
|
||||
String response = IOUtils.toString(http.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
assertThat(response, not(containsString("_format")));
|
||||
assertEquals(200, http.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
http.close();
|
||||
}
|
||||
|
||||
restServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle4() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
ourLog.info(response);
|
||||
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
|
||||
IdDt id = new IdDt(bundleResp.getEntry().get(0).getResponse().getLocation());
|
||||
assertEquals("Patient", id.getResourceType());
|
||||
assertTrue(id.hasIdPart());
|
||||
assertTrue(id.isIdPartValidLong());
|
||||
assertTrue(id.hasVersionIdPart());
|
||||
assertTrue(id.isVersionIdPartValidLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle5() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
try {
|
||||
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals("processing", oo.getIssue().get(0).getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle6() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
// try {
|
||||
// fail();
|
||||
// } catch (InvalidRequestException e) {
|
||||
// OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
// assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
|
||||
// assertEquals("processing", oo.getIssue().get(0).getCode());
|
||||
// }
|
||||
public void testGetOperationDefinition() {
|
||||
OperationDefinition op = ourClient.read(OperationDefinition.class, "get-resource-counts");
|
||||
assertEquals("$get-resource-counts", op.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -149,24 +159,6 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
|
|||
ourLog.info(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is Gramahe's test transaction - it requires some set up in order to work
|
||||
*/
|
||||
// @Test
|
||||
public void testTransactionFromBundle3() throws Exception {
|
||||
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
ourLog.info(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOperationDefinition() {
|
||||
OperationDefinition op = ourClient.read(OperationDefinition.class, "get-resource-counts");
|
||||
assertEquals("$get-resource-counts", op.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle2() throws Exception {
|
||||
|
||||
|
@ -204,65 +196,108 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
|
|||
assertEquals(id1_4.toVersionless(), id2_4.toVersionless());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is Gramahe's test transaction - it requires some set up in order to work
|
||||
*/
|
||||
// @Test
|
||||
public void testTransactionFromBundle3() throws Exception {
|
||||
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
ourLog.info(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle4() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
ourLog.info(response);
|
||||
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
|
||||
IdDt id = new IdDt(bundleResp.getEntry().get(0).getResponse().getLocation());
|
||||
assertEquals("Patient", id.getResourceType());
|
||||
assertTrue(id.hasIdPart());
|
||||
assertTrue(id.isIdPartValidLong());
|
||||
assertTrue(id.hasVersionIdPart());
|
||||
assertTrue(id.isVersionIdPartValidLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle5() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
try {
|
||||
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals("processing", oo.getIssue().get(0).getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle6() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
|
||||
String bundle = IOUtils.toString(bundleRes);
|
||||
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
// try {
|
||||
// fail();
|
||||
// } catch (InvalidRequestException e) {
|
||||
// OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||
// assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
|
||||
// assertEquals("processing", oo.getIssue().get(0).getCode());
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionSearch() throws Exception {
|
||||
for (int i = 0; i < 20; i ++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT_" + i);
|
||||
myPatientDao.create(p);
|
||||
}
|
||||
|
||||
Bundle req = new Bundle();
|
||||
req.setType(BundleTypeEnum.TRANSACTION);
|
||||
req.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?");
|
||||
Bundle resp = ourClient.transaction().withBundle(req).execute();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
assertEquals(1, resp.getEntry().size());
|
||||
Bundle respSub = (Bundle)resp.getEntry().get(0).getResource();
|
||||
assertEquals("self", respSub.getLink().get(0).getRelation());
|
||||
assertEquals(ourServerBase + "/Patient", respSub.getLink().get(0).getUrl());
|
||||
assertEquals("next", respSub.getLink().get(1).getRelation());
|
||||
assertThat(respSub.getLink().get(1).getUrl(), containsString("/fhir/context?_getpages"));
|
||||
assertThat(respSub.getEntry().get(0).getFullUrl(), startsWith(ourServerBase + "/Patient/"));
|
||||
assertEquals(Patient.class, respSub.getEntry().get(0).getResource().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCount() throws Exception {
|
||||
for (int i = 0; i < 20; i ++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT_" + i);
|
||||
myPatientDao.create(p);
|
||||
}
|
||||
|
||||
Bundle req = new Bundle();
|
||||
req.setType(BundleTypeEnum.TRANSACTION);
|
||||
req.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?_summary=count");
|
||||
Bundle resp = ourClient.transaction().withBundle(req).execute();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
assertEquals(1, resp.getEntry().size());
|
||||
Bundle respSub = (Bundle)resp.getEntry().get(0).getResource();
|
||||
assertEquals(20, respSub.getTotal().intValue());
|
||||
assertEquals(0, respSub.getEntry().size());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
ourAppCtx.stop();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourAppCtx = new AnnotationConfigApplicationContext(TestDstu2Config.class);
|
||||
|
||||
IFhirResourceDao<Patient> patientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDaoDstu2", IFhirResourceDao.class);
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(patientDao);
|
||||
|
||||
IFhirResourceDao<Questionnaire> questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class);
|
||||
QuestionnaireResourceProviderDstu2 questionnaireRp = new QuestionnaireResourceProviderDstu2();
|
||||
questionnaireRp.setDao(questionnaireDao);
|
||||
|
||||
IFhirResourceDao<Observation> observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDaoDstu2", IFhirResourceDao.class);
|
||||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(observationDao);
|
||||
|
||||
IFhirResourceDao<Organization> organizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDaoDstu2", IFhirResourceDao.class);
|
||||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(organizationDao);
|
||||
|
||||
restServer = new RestfulServer(ourCtx);
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
JpaSystemProviderDstu2 systemProv = ourAppCtx.getBean(JpaSystemProviderDstu2.class, "mySystemProviderDstu2");
|
||||
restServer.setPlainProviders(systemProv);
|
||||
|
||||
int myPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(myPort);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
restServer.setFhirContext(ourCtx);
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourHttpClient = builder.build();
|
||||
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||
ourClient.setLogRequestAndResponse(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
<context:mbean-server />
|
||||
|
||||
<bean depends-on="dbServer" id="myPersistenceDataSourceDstu1" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||
<!-- ;create=true /opt/glassfish/glassfish4/glassfish/nodes/localhost-domain1/fhirtest/fhirdb -->
|
||||
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
|
||||
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
||||
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
|
||||
<property name="url" value="jdbc:derby://localhost:1527/#{systemProperties['fhir.db.location']};create=true" />
|
||||
<!-- <property name="url" value="jdbc:derby://localhost:1527//opt/glassfish/fhirtest/fhirtest;create=true" /> -->
|
||||
<!--<property name="url" value="jdbc:derby://localhost:1527#{systemProperties['fhir.db.location']};create=true" />-->
|
||||
<property name="username" value="SA"/>
|
||||
<property name="password" value="SA"/>
|
||||
</bean>
|
||||
|
||||
<bean depends-on="dbServer" id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
<property name="dataSource" ref="myPersistenceDataSourceDstu1" />
|
||||
<!--
|
||||
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />
|
||||
-->
|
||||
<property name="packagesToScan">
|
||||
<list>
|
||||
<value>ca.uhn.fhir.jpa.entity</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="persistenceUnitName" value="FHIR_DSTU2" />
|
||||
<property name="jpaVendorAdapter">
|
||||
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
||||
<property name="showSql" value="false" />
|
||||
<property name="generateDdl" value="true" />
|
||||
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="jpaPropertyMap">
|
||||
<map>
|
||||
<entry key="hibernate.dialect" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
|
||||
<entry key="hibernate.hbm2ddl.auto" value="update" />
|
||||
<entry key="hibernate.jdbc.batch_size" value="20" />
|
||||
<entry key="hibernate.cache.use_minimal_puts" value="true" />
|
||||
<entry key="hibernate.show_sql" value="false" />
|
||||
<entry key="hibernate.cache.use_query_cache" value="false" />
|
||||
<entry key="hibernate.cache.use_second_level_cache" value="false" />
|
||||
<entry key="hibernate.cache.use_structured_entries" value="false" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!--for mysql-->
|
||||
<!--
|
||||
<bean depends-on="dbServer" id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
|
||||
<property name="url" value="jdbc:mysql://fhirdb.url/fhirdbname" />
|
||||
<property name="username" value="sa"/>
|
||||
<property name="password" value="sa"/>
|
||||
<property name="testOnBorrow" value="true"/>
|
||||
<property name="validationQuery" value="select 1;"/>
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
|
@ -1,69 +0,0 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
<context:mbean-server />
|
||||
|
||||
<bean depends-on="dbServer" id="myPersistenceDataSourceDstu2" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
|
||||
<property name="url" value="jdbc:derby://localhost:1527/#{systemProperties['fhir.db.location.dstu2']};create=true" />
|
||||
<property name="username" value="SA" />
|
||||
<property name="password" value="SA" />
|
||||
</bean>
|
||||
|
||||
<!--for mysql -->
|
||||
<!-- <bean depends-on="dbServer" id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName"
|
||||
value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://fhirdb.url/fhirdbname" /> <property name="username" value="sa"/> <property name="password"
|
||||
value="sa"/> <property name="testOnBorrow" value="true"/> <property name="validationQuery" value="select 1;"/> </bean> -->
|
||||
|
||||
<bean depends-on="dbServer" id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
<property name="dataSource" ref="myPersistenceDataSourceDstu2" />
|
||||
<!--
|
||||
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />
|
||||
-->
|
||||
<property name="persistenceUnitName" value="FHIR_DSTU2" />
|
||||
<property name="packagesToScan">
|
||||
<list>
|
||||
<value>ca.uhn.fhir.jpa.entity</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="jpaVendorAdapter">
|
||||
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
||||
<property name="showSql" value="false" />
|
||||
<property name="generateDdl" value="true" />
|
||||
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||
<!-- <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />-->
|
||||
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
|
||||
<!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> -->
|
||||
</bean>
|
||||
</property>
|
||||
<property name="jpaPropertyMap">
|
||||
<map>
|
||||
<entry key="hibernate.dialect" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
|
||||
<entry key="hibernate.hbm2ddl.auto" value="update" />
|
||||
<entry key="hibernate.jdbc.batch_size" value="20" />
|
||||
<entry key="hibernate.cache.use_minimal_puts" value="true" />
|
||||
<entry key="hibernate.show_sql" value="false" />
|
||||
<entry key="hibernate.cache.use_query_cache" value="false" />
|
||||
<entry key="hibernate.cache.use_second_level_cache" value="false" />
|
||||
<entry key="hibernate.cache.use_structured_entries" value="false" />
|
||||
<entry key="hibernate.search.default.directory_provider" value="filesystem" />
|
||||
<entry key="hibernate.search.default.indexBase" value="#{systemProperties['fhir.lucene.location.dstu2']}" />
|
||||
<entry key="hibernate.search.lucene_version" value="LUCENE_CURRENT" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
|
||||
<property name="entityManagerFactory" ref="entityManagerFactory" />
|
||||
</bean>
|
||||
<tx:annotation-driven transaction-manager="transactionManager" />
|
||||
|
||||
</beans>
|
|
@ -11,7 +11,7 @@
|
|||
</button>
|
||||
<a class="navbar-brand"
|
||||
th:href="'home?encoding=' + ${encoding} + '&pretty=' + ${pretty}">
|
||||
<i class="fa fa-home topbarIcon" /> HAPI FHIR
|
||||
<i class="fa fa-home topbarIcon" /> Home
|
||||
</a>
|
||||
<a class="navbar-left navbarBreadcrumb hidden-xs hidden-sm"
|
||||
th:if="${resourceName} != null and ${resourceName.empty} == false"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<li>Jetty (JPA, CLI, Public Server): 9.2.6.v20141205 -> 9.3.4.v20151007 </li>
|
||||
</ul>
|
||||
]]>
|
||||
</action>
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA and Tester Overlay now use Spring Java config files instead
|
||||
of the older XML config files. All example projects have been updated.
|
||||
|
@ -183,6 +183,11 @@
|
|||
Fix a crash when encoding a Binary resource in JSON encoding
|
||||
if the resource has no content-type
|
||||
</action>
|
||||
<action type="fix">
|
||||
JPA server now supports read/history/search in transaction entries
|
||||
by calling the actual implementing method in the server (previously
|
||||
the call was simulated, which meant that many features did not work)
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue