Work on support for new bundle format in DSTU2
This commit is contained in:
parent
6580abe5f2
commit
db65d5db59
|
@ -28,7 +28,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.hl7.fhir.instance.model.IBase;
|
||||
|
|
|
@ -30,6 +30,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public interface IFhirVersion {
|
||||
|
@ -52,5 +53,6 @@ public interface IFhirVersion {
|
|||
|
||||
BaseCodingDt newCodingDt();
|
||||
|
||||
IVersionSpecificBundleFactory newBundleFactory();
|
||||
|
||||
}
|
||||
|
|
|
@ -1259,8 +1259,10 @@ class ParserState<T> {
|
|||
push(new BundleLinkState(myInstance));
|
||||
} else if ("entry".equals(theLocalPart)) {
|
||||
push(new BundleEntryState(myInstance, myResourceType));
|
||||
} else if ("text".equals(theLocalPart)) {
|
||||
push(new SwallowChildrenWholeState(getPreResourceState()));
|
||||
} else {
|
||||
throw new DataFormatException("Unxpected element '" + theLocalPart + " in element 'Bundle'");
|
||||
throw new DataFormatException("Unxpected element '" + theLocalPart + "' in element 'Bundle'");
|
||||
}
|
||||
|
||||
// if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
|
||||
|
|
|
@ -85,6 +85,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
|
||||
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
|
||||
import ca.uhn.fhir.util.XmlUtil;
|
||||
|
@ -244,12 +245,12 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
|
||||
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
|
||||
|
||||
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
|
||||
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
|
||||
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
|
||||
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
|
||||
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
|
||||
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
|
||||
writeAtomLink(eventWriter, Constants.LINK_SELF, theBundle.getLinkSelf());
|
||||
writeAtomLink(eventWriter, Constants.LINK_FIRST, theBundle.getLinkFirst());
|
||||
writeAtomLink(eventWriter, Constants.LINK_PREVIOUS, theBundle.getLinkPrevious());
|
||||
writeAtomLink(eventWriter, Constants.LINK_NEXT, theBundle.getLinkNext());
|
||||
writeAtomLink(eventWriter, Constants.LINK_LAST, theBundle.getLinkLast());
|
||||
writeAtomLink(eventWriter, Constants.LINK_FHIR_BASE, theBundle.getLinkBase());
|
||||
|
||||
if (theBundle.getTotalResults().getValue() != null) {
|
||||
eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.apache.http.entity.ByteArrayEntity;
|
|||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
|
@ -47,7 +48,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
/**
|
||||
|
@ -168,7 +169,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
myParams = theParams;
|
||||
myBundleType = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
|
||||
StringBuilder url = new StringBuilder();
|
||||
|
@ -235,9 +236,17 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
contents = parser.encodeBundleToString(myBundle);
|
||||
contentType = encoding.getBundleContentType();
|
||||
} else if (myResources != null) {
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
|
||||
contents = parser.encodeBundleToString(bundle);
|
||||
contentType = encoding.getBundleContentType();
|
||||
IVersionSpecificBundleFactory bundleFactory = myContext.getVersion().newBundleFactory();
|
||||
bundleFactory.initializeBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
|
||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||
if (bundle != null) {
|
||||
contents = parser.encodeBundleToString(bundle);
|
||||
contentType = encoding.getBundleContentType();
|
||||
} else {
|
||||
IBaseResource bundleRes = bundleFactory.getResourceBundle();
|
||||
contents = parser.encodeResourceToString(bundleRes);
|
||||
contentType = encoding.getResourceContentType();
|
||||
}
|
||||
} else if (myContents != null) {
|
||||
contents = myContents;
|
||||
if (myContentsIsBundle) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
|||
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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
@ -72,7 +73,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
set.add(Constants.PARAM_COUNT);
|
||||
ALLOWED_PARAMS = Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
|
||||
private MethodReturnTypeEnum myMethodReturnType;
|
||||
private Class<?> myResourceListCollectionType;
|
||||
private String myResourceName;
|
||||
|
@ -89,13 +90,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
Class<?> collectionType = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
||||
if (collectionType != null) {
|
||||
if (!Object.class.equals(collectionType) && !IResource.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;
|
||||
|
||||
} else if (IBaseResource.class.isAssignableFrom(methodReturnType)) {
|
||||
if (Modifier.isAbstract(methodReturnType.getModifiers())==false && theContext.getResourceDefinition((Class<? extends IBaseResource>) methodReturnType).isBundle()) {
|
||||
if (Modifier.isAbstract(methodReturnType.getModifiers()) == false && theContext.getResourceDefinition((Class<? extends IBaseResource>) methodReturnType).isBundle()) {
|
||||
myMethodReturnType = MethodReturnTypeEnum.BUNDLE_RESOURCE;
|
||||
} else {
|
||||
myMethodReturnType = MethodReturnTypeEnum.RESOURCE;
|
||||
|
@ -105,7 +107,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
} else if (IBundleProvider.class.isAssignableFrom(methodReturnType)) {
|
||||
myMethodReturnType = MethodReturnTypeEnum.BUNDLE_PROVIDER;
|
||||
} 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) {
|
||||
|
@ -113,8 +116,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
|
||||
if (resourceDefAnnotation == null) {
|
||||
if (Modifier.isAbstract(theReturnResourceType.getModifiers())) {
|
||||
// If we're returning an abstract type, that's ok
|
||||
}else {
|
||||
// If we're returning an abstract type, that's ok
|
||||
} else {
|
||||
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
|
||||
}
|
||||
} else {
|
||||
|
@ -246,29 +249,53 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
Object resultObj = invokeServer(theRequest, params);
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE:{
|
||||
case BUNDLE: {
|
||||
|
||||
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
|
||||
IResource resource = (IResource) resultObj;
|
||||
IResource resource;
|
||||
if (resultObj instanceof IBundleProvider) {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
resource = result.getResources(0, 1).get(0);
|
||||
} else {
|
||||
resource = (IResource) resultObj;
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||
break;
|
||||
} else {
|
||||
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null, getResponseBundleType());
|
||||
|
||||
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) {
|
||||
return;
|
||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().getVersion().newBundleFactory();
|
||||
bundleFactory.initializeBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null,
|
||||
getResponseBundleType());
|
||||
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, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
} 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, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode,
|
||||
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), theRequest.getFhirServerBase());
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
case RESOURCE:{
|
||||
case RESOURCE: {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
if (result.size() == 0) {
|
||||
throw new ResourceNotFoundException(theRequest.getId());
|
||||
|
|
|
@ -113,6 +113,12 @@ public class Constants {
|
|||
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();
|
||||
public static final String LINK_SELF = "self";
|
||||
public static final String LINK_FIRST = "first";
|
||||
public static final String LINK_PREVIOUS = "previous";
|
||||
public static final String LINK_NEXT = "next";
|
||||
public static final String LINK_LAST = "last";
|
||||
public static final String LINK_FHIR_BASE = "fhir-base";
|
||||
|
||||
static {
|
||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
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.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu1BundleFactory.class);
|
||||
private Bundle myBundle;
|
||||
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase) {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
myBundle.addResource(next, theContext, theServerBase);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
BundleEntry entry = myBundle.addResource(next, theContext, theServerBase);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||
if (entry.getSearchMode().isEmpty()) {
|
||||
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType) {
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IResource> resourceList;
|
||||
if (theServer.getPagingProvider() == null) {
|
||||
numToReturn = theResult.size();
|
||||
resourceList = theResult.getResources(0, numToReturn);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
} else {
|
||||
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||
if (theLimit == null) {
|
||||
numToReturn = pagingProvider.getDefaultPageSize();
|
||||
} else {
|
||||
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
|
||||
}
|
||||
|
||||
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
if (theSearchId != null) {
|
||||
searchId = theSearchId;
|
||||
} else {
|
||||
if (theResult.size() > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : resourceList) {
|
||||
if (next.getId() == null || next.getId().isEmpty()) {
|
||||
if (!(next instanceof BaseOperationOutcome)) {
|
||||
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (IResource nextRes : resourceList) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addResourcesToBundle(theServer.getFhirContext(), resourceList, theBundleType, theServerBase);
|
||||
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
|
||||
|
||||
myBundle.setPublished(theResult.getPublished());
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
|
||||
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
|
||||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
myBundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
myBundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
if (myBundle.getAuthorName().isEmpty()) {
|
||||
myBundle.getAuthorName().setValue(theAuthor);
|
||||
}
|
||||
|
||||
if (myBundle.getBundleId().isEmpty()) {
|
||||
myBundle.getBundleId().setValue(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
if (myBundle.getPublished().isEmpty()) {
|
||||
myBundle.getPublished().setToCurrentTimeInLocalTimeZone();
|
||||
}
|
||||
|
||||
if (myBundle.getLinkBase().isEmpty()) {
|
||||
myBundle.getLinkBase().setValue(theServerBase);
|
||||
}
|
||||
|
||||
if (myBundle.getLinkSelf().isEmpty()) {
|
||||
myBundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
}
|
||||
|
||||
if (theBundleType != null && myBundle.getType().isEmpty()) {
|
||||
myBundle.getType().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
if (myBundle.getTotalResults().isEmpty()) {
|
||||
myBundle.getTotalResults().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getDstu1Bundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
myBundle.getAuthorName().setValue(theAuthor);
|
||||
myBundle.getBundleId().setValue(UUID.randomUUID().toString());
|
||||
myBundle.getPublished().setToCurrentTimeInLocalTimeZone();
|
||||
myBundle.getLinkBase().setValue(theServerBase);
|
||||
myBundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
myBundle.getType().setValueAsEnum(theBundleType);
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
myBundle.addResource(next, theContext, theServerBase);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
BundleEntry entry = myBundle.addResource(next, theContext, theServerBase);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||
if (entry.getSearchMode().isEmpty()) {
|
||||
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myBundle.getTotalResults().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,11 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
|
||||
public interface IBundleProvider {
|
||||
|
||||
/**
|
||||
* @param theFromIndex The low index (inclusive) to return
|
||||
* @param theToIndex The high index (exclusive) to return
|
||||
* @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
|
||||
*/
|
||||
List<IResource> getResources(int theFromIndex, int theToIndex);
|
||||
|
||||
int size();
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
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.valueset.BundleTypeEnum;
|
||||
|
||||
public interface IVersionSpecificBundleFactory {
|
||||
|
||||
void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase);
|
||||
|
||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
|
||||
|
||||
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
||||
int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType);
|
||||
|
||||
Bundle getDstu1Bundle();
|
||||
|
||||
IBaseResource getResourceBundle();
|
||||
|
||||
void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
|
||||
|
||||
}
|
|
@ -51,6 +51,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -79,7 +80,7 @@ public class RestfulServer extends HttpServlet {
|
|||
* Default setting for {@link #setETagSupport(ETagSupportEnum) ETag Support}: {@link ETagSupportEnum#ENABLED}
|
||||
*/
|
||||
public static final ETagSupportEnum DEFAULT_ETAG_SUPPORT = ETagSupportEnum.ENABLED;
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private AddProfileTagEnum myAddProfileTag;
|
||||
|
@ -447,20 +448,34 @@ public class RestfulServer extends HttpServlet {
|
|||
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequest);
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint,
|
||||
start, count, thePagingAction, null);
|
||||
IVersionSpecificBundleFactory bundleFactory = myFhirContext.getVersion().newBundleFactory();
|
||||
bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, start, count, thePagingAction,
|
||||
null);
|
||||
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||
if (bundle != null) {
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = 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(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
} else {
|
||||
IBaseResource resBundle = bundleFactory.getResourceBundle();
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = 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(this, theResponse, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK,
|
||||
theRequest.isRespondGzip(), theRequest.getFhirServerBase());
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
|
||||
}
|
||||
|
||||
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||
|
@ -816,9 +831,9 @@ public class RestfulServer extends HttpServlet {
|
|||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
*
|
||||
* @throws ServletException If the initialization failed. Note that you should consider throwing
|
||||
* {@link UnavailableException} (which extends {@link ServletException}), as this is a flag
|
||||
* to the servlet container that the servlet is not usable.
|
||||
* @throws ServletException
|
||||
* If the initialization failed. Note that you should consider throwing {@link UnavailableException} (which extends {@link ServletException}), as this is a flag to the servlet
|
||||
* container that the servlet is not usable.
|
||||
*/
|
||||
protected void initialize() throws ServletException {
|
||||
// nothing by default
|
||||
|
|
|
@ -7,13 +7,9 @@ import java.io.OutputStreamWriter;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
@ -21,32 +17,26 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.client.utils.DateUtils;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.base.resource.BaseBinary;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class RestfulServerUtils {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class);
|
||||
|
||||
static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
Integer count = null;
|
||||
|
@ -54,13 +44,13 @@ public class RestfulServerUtils {
|
|||
try {
|
||||
count = Integer.parseInt(countString);
|
||||
} catch (NumberFormatException e) {
|
||||
RestfulServer.ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip,
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip,
|
||||
String theServerBase) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
|
@ -240,7 +230,7 @@ public class RestfulServerUtils {
|
|||
return b.toString();
|
||||
}
|
||||
|
||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
public static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
|
||||
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||
if (tl == null) {
|
||||
|
@ -255,101 +245,6 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.getAuthorName().setValue(theAuthor);
|
||||
bundle.getBundleId().setValue(UUID.randomUUID().toString());
|
||||
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
|
||||
bundle.getLinkBase().setValue(theServerBase);
|
||||
bundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
bundle.getType().setValueAsEnum(theBundleType);
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
RestfulServer.ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
RestfulServer.ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
BundleEntry entry = bundle.addResource(next, theContext, theServerBase);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||
if (entry.getSearchMode().isEmpty()) {
|
||||
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bundle.getTotalResults().setValue(theTotalResults);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
|
@ -427,8 +322,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
|
||||
public static Integer extractCountParameter(HttpServletRequest theRequest) {
|
||||
String name = Constants.PARAM_COUNT;
|
||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, name);
|
||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, EncodingEnum theResponseEncoding, String theServerBase, boolean thePrettyPrint, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, boolean theRequestIsBrowser) throws IOException {
|
||||
|
@ -471,81 +365,12 @@ public class RestfulServerUtils {
|
|||
RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
||||
}
|
||||
|
||||
private static void validateResourceListNotNull(List<IResource> theResourceList) {
|
||||
public static void validateResourceListNotNull(List<IResource> theResourceList) {
|
||||
if (theResourceList == null) {
|
||||
throw new InternalErrorException("IBundleProvider returned a null list of resources - This is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType) {
|
||||
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IResource> resourceList;
|
||||
if (theServer.getPagingProvider() == null) {
|
||||
numToReturn = theResult.size();
|
||||
resourceList = theResult.getResources(0, numToReturn);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
} else {
|
||||
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||
if (theLimit == null) {
|
||||
numToReturn = pagingProvider.getDefaultPageSize();
|
||||
} else {
|
||||
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
|
||||
}
|
||||
|
||||
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
if (theSearchId != null) {
|
||||
searchId = theSearchId;
|
||||
} else {
|
||||
if (theResult.size() > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : resourceList) {
|
||||
if (next.getId() == null || next.getId().isEmpty()) {
|
||||
if (!(next instanceof BaseOperationOutcome)) {
|
||||
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (IResource nextRes : resourceList) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
|
||||
|
||||
bundle.setPublished(theResult.getPublished());
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
|
||||
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
|
||||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
bundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
bundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -26,8 +26,9 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
|
@ -146,7 +147,7 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
|
||||
* client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,8 +26,9 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
|
@ -59,7 +60,7 @@ public class InterceptorAdapter implements IServerInterceptor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
|
@ -62,7 +62,7 @@ public class ResponseValidatingInterceptor extends InterceptorAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildDirectResource;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
|
@ -72,8 +73,8 @@ public class FhirTerser {
|
|||
* well as any contained resources.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not decend into linked resources (e.g.
|
||||
* {@link BaseResourceReferenceDt#getResource()})
|
||||
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
|
||||
* {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
|
||||
* </p>
|
||||
*
|
||||
* @param theResource
|
||||
|
@ -191,6 +192,9 @@ public class FhirTerser {
|
|||
// }
|
||||
|
||||
switch (theDefinition.getChildType()) {
|
||||
// case RESOURCE:
|
||||
// // Don't descend into embedded resources
|
||||
// break;
|
||||
case PRIMITIVE_XHTML:
|
||||
case PRIMITIVE_DATATYPE:
|
||||
// These are primitive types
|
||||
|
@ -205,9 +209,14 @@ public class FhirTerser {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case RESOURCE:
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE:
|
||||
case RESOURCE: {
|
||||
case COMPOSITE_DATATYPE: {
|
||||
if (theChildDefinition instanceof RuntimeChildDirectResource) {
|
||||
// Don't descend into embedded resources
|
||||
return;
|
||||
}
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
|
||||
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
|
||||
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
|
||||
|
@ -264,6 +273,10 @@ public class FhirTerser {
|
|||
/**
|
||||
* Visit all elements in a given resource
|
||||
*
|
||||
* <p>
|
||||
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
|
||||
* {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
|
||||
* </p>
|
||||
* @param theResource
|
||||
* The resource to visit
|
||||
* @param theVisitor
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
</attributes>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<installed facet="jst.utility" version="1.0"/>
|
||||
<installed facet="java" version="1.6"/>
|
||||
<installed facet="java" version="1.7"/>
|
||||
</faceted-project>
|
||||
|
|
|
@ -514,7 +514,7 @@ public class ResourceProviderDstu2Test {
|
|||
assertEquals(1, actual.getContained().getContainedResources().size());
|
||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSaveAndRetrieveWithContained01</td>"));
|
||||
|
||||
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01")).execute();
|
||||
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01")).prettyPrint().execute();
|
||||
assertEquals(1, b.size());
|
||||
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<property name="servers">
|
||||
<list>
|
||||
<value>home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1</value>
|
||||
<value>home_dev , DEV , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2</value>
|
||||
<value>home_dev , DSTU2 , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2</value>
|
||||
<value>hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open</value>
|
||||
<value>hidev , DEV , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
|
||||
<value>hidev , DSTU2 , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
|
||||
<value>furore , DSTU1 , Spark - Furore , http://spark.furore.com/fhir</value>
|
||||
<value>blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
|
||||
<value>oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190</value>
|
||||
|
|
|
@ -37,7 +37,9 @@ import ca.uhn.fhir.model.dev.composite.ContainedDt;
|
|||
import ca.uhn.fhir.model.dev.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dev.resource.Profile;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.dev.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.dev.ServerProfileProvider;
|
||||
|
@ -109,4 +111,9 @@ public class FhirDev implements IFhirVersion {
|
|||
return new CodingDt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
return new Dstu1BundleFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -75,7 +75,9 @@ import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
|
|||
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
||||
|
@ -384,5 +386,10 @@ public class FhirDstu1 implements IFhirVersion {
|
|||
return new CodingDt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
return new Dstu1BundleFactory();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
|
@ -156,7 +157,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|||
* Intercept the outgoing response to perform auditing of the request data if the resource is auditable.
|
||||
*/
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
if(myClientParamsOptional && myDataStore == null){
|
||||
//auditing is not required or configured, so do nothing here
|
||||
|
@ -180,7 +181,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|||
|
||||
byte[] query = getQueryFromRequestDetails(theRequestDetails);
|
||||
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
|
||||
ObjectElement auditableObject = getObjectElement(theResponseObject, lifecycle , query);
|
||||
ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle , query);
|
||||
if(auditableObject == null){
|
||||
log.debug("No auditable resources to audit");
|
||||
return true;
|
||||
|
|
|
@ -15,7 +15,9 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
|
||||
|
@ -34,6 +36,7 @@ import ca.uhn.fhir.model.dstu.valueset.ConditionStatusEnum;
|
|||
import ca.uhn.fhir.model.dstu.valueset.NameUseEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.PractitionerRoleEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
||||
/**
|
||||
|
@ -197,7 +200,10 @@ public class ContainedResourceEncodingTest {
|
|||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
|
||||
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
|
||||
factory.initializeBundleFromResourceList(ctx, "", list, "http://foo", "http://foo", 2, null);
|
||||
Bundle bundle = factory.getDstu1Bundle();
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
|
@ -236,8 +242,11 @@ public class ContainedResourceEncodingTest {
|
|||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
|
||||
|
||||
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
|
||||
factory.initializeBundleFromResourceList(ctx, "", list, "http://foo", "http://foo", 2, null);
|
||||
Bundle bundle = factory.getDstu1Bundle();
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
Assert.assertTrue(xml.contains("Mueller"));
|
||||
|
|
|
@ -2,7 +2,10 @@ package ca.uhn.fhir.rest.server;
|
|||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
@ -16,7 +19,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ca.uhn.fhir.model.dstu.resource.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
|
@ -40,6 +43,7 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -49,6 +53,13 @@ import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
|||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.AdverseReaction;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.resource.Profile;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
|
@ -74,8 +85,6 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
|
@ -104,8 +113,9 @@ public class RestfulServerMethodTest {
|
|||
|
||||
p.getManagingOrganization().setResource(o);
|
||||
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
|
||||
factory.initializeBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
|
||||
assertEquals(2, factory.getDstu1Bundle().getEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -39,7 +39,9 @@ import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
|||
import ca.uhn.fhir.model.dstu2.resource.Profile;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.dstu2.Dstu2BundleFactory;
|
||||
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.dstu2.ServerProfileProvider;
|
||||
|
||||
|
@ -109,5 +111,10 @@ public class FhirDstu2 implements IFhirVersion {
|
|||
return new CodingDt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
return new Dstu2BundleFactory();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
package ca.uhn.fhir.rest.server.provider.dstu2;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.base.resource.BaseOperationOutcome;
|
||||
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.Link;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
|
||||
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.IPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class);
|
||||
private Bundle myBundle;
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase) {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
Entry entry = myBundle.addEntry().setResource(next);
|
||||
|
||||
BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValue(searchMode.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
myBundle.addEntry().setResource(next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
|
||||
if (myBundle.getId().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
if (ResourceMetadataKeyEnum.PUBLISHED.get(myBundle) == null) {
|
||||
InstantDt published = new InstantDt();
|
||||
published.setToCurrentTimeInLocalTimeZone();
|
||||
ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, published);
|
||||
}
|
||||
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) {
|
||||
myBundle.addLink().setRelation("self").setUrl(theCompleteUrl);
|
||||
}
|
||||
|
||||
if (!hasLink(Constants.LINK_FHIR_BASE, myBundle) && isNotBlank(theServerBase)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
}
|
||||
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults > 0) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (Link next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType) {
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IResource> resourceList;
|
||||
if (theServer.getPagingProvider() == null) {
|
||||
numToReturn = theResult.size();
|
||||
resourceList = theResult.getResources(0, numToReturn);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
} else {
|
||||
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||
if (theLimit == null) {
|
||||
numToReturn = pagingProvider.getDefaultPageSize();
|
||||
} else {
|
||||
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
|
||||
}
|
||||
|
||||
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
if (theSearchId != null) {
|
||||
searchId = theSearchId;
|
||||
} else {
|
||||
if (theResult.size() > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : resourceList) {
|
||||
if (next.getId() == null || next.getId().isEmpty()) {
|
||||
if (!(next instanceof BaseOperationOutcome)) {
|
||||
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (IResource nextRes : resourceList) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addResourcesToBundle(theServer.getFhirContext(), resourceList, theBundleType, theServerBase);
|
||||
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
|
||||
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
|
||||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, InstantDt.withCurrentTime());
|
||||
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
myBundle.addEntry().setResource(next);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
myBundle.addEntry().setResource(next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
|
||||
}
|
||||
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -162,7 +162,7 @@ public class BinaryTest {
|
|||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_ATOM_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
|
||||
assertEquals(Constants.CT_FHIR_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ public class BundleTypeInResponseTest {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(1, bundle.getEntries().size());
|
||||
assertEquals(BundleTypeEnum.SEARCHSET, bundle.getType().getValueAsEnum());
|
||||
|
|
|
@ -35,13 +35,14 @@ public class SearchWithDstu2BundleTest {
|
|||
@Test
|
||||
public void testSearch() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true");
|
||||
// HttpResponse status = ourClient.execute(httpGet);
|
||||
// String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
// IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
//
|
||||
// assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
//
|
||||
// ourLog.info(responseContent);
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,7 +69,7 @@ public class SearchWithDstu2BundleTest {
|
|||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
|
|
@ -179,7 +179,7 @@ public class TransactionWithBundleResourceParamTest {
|
|||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(500000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
||||
public class FhirTerserTest {
|
||||
|
||||
private static FhirContext ourCtx = new FhirContext();
|
||||
|
||||
@Test
|
||||
public void testGetAllPopulatedChildElementsOfTypeDoesntDescendIntoEmbedded() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT");
|
||||
|
||||
Bundle b = new Bundle();
|
||||
b.addEntry().setResource(p);
|
||||
b.addLink().setRelation("BUNDLE");
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
List<StringDt> strings = t.getAllPopulatedChildElementsOfType(b, StringDt.class);
|
||||
|
||||
assertEquals(1, strings.size());
|
||||
assertThat(strings, containsInAnyOrder(new StringDt("BUNDLE")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllPopulatedChildElementsOfTypeDescendsIntoContained() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT");
|
||||
|
||||
Organization o = new Organization();
|
||||
o.getNameElement().setValue("ORGANIZATION");
|
||||
p.getContained().getContainedResources().add(o);
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
List<StringDt> strings = t.getAllPopulatedChildElementsOfType(p, StringDt.class);
|
||||
|
||||
assertEquals(2, strings.size());
|
||||
assertThat(strings, containsInAnyOrder(new StringDt("PATIENT"), new StringDt("ORGANIZATION")));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.model.api.IFhirVersion;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public class FhirDstu2Hl7Org implements IFhirVersion {
|
||||
|
@ -105,4 +106,9 @@ public class FhirDstu2Hl7Org implements IFhirVersion {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import ca.uhn.fhir.model.dev.resource.Profile;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public class FhirDev implements IFhirVersion {
|
||||
|
@ -115,5 +116,9 @@ public class FhirDev implements IFhirVersion {
|
|||
return new CodingDt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public class FhirDstu1 implements IFhirVersion {
|
||||
|
@ -384,4 +385,10 @@ public class FhirDstu1 implements IFhirVersion {
|
|||
return new ca.uhn.fhir.model.dstu.composite.CodingDt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -34,8 +34,10 @@ import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
|||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public class FhirDstu2 implements IFhirVersion {
|
||||
|
@ -100,4 +102,10 @@ public class FhirDstu2 implements IFhirVersion {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IVersionSpecificBundleFactory newBundleFactory() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
<property name="servers">
|
||||
<list>
|
||||
<!-- Each value is a reference to one FHIR server -->
|
||||
<!-- Format is: [id],[name],[base URL] -->
|
||||
<!-- Format is: [id],[FHIR version: DSTU1|DSTU2],[name],[base URL] -->
|
||||
<!-- The URL should be customized to match the base URL for the FHIR endpoint -->
|
||||
<value>example , Restful Server Example , http://localhost:8080/fhir</value>
|
||||
<value>example , DSTU1 , Restful Server Example , http://localhost:8080/fhir</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
|
||||
|
||||
<properties>
|
||||
<title>Server Tester - HAPI FHIR</title>
|
||||
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
|
||||
</properties>
|
||||
|
||||
<body>
|
||||
|
||||
<section name="FHIR Tester Web Application">
|
||||
|
||||
<macro name="toc">
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
HAPI FHIR includes a web UI that can be used to test your server implementation.
|
||||
This UI is the same UI used on the <a href="http://fhirtest.uhn.ca">http://fhirtest.uhn.ca</a>
|
||||
public server.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The Tester is a
|
||||
<a href="http://maven.apache.org/plugins/maven-war-plugin/overlays.html">Maven WAR Overlay</a>,
|
||||
meaning that you create your own WAR project (which you would likely be doing anyway
|
||||
to create your server) and the overlay drops a number of files into your project.
|
||||
</p>
|
||||
|
||||
<subsection name="Adding the Overlay">
|
||||
|
||||
<p>
|
||||
These instructions assume that you have an exsiting web project
|
||||
which uses Maven to build. The POM.xml should have a "packaging"
|
||||
type of "war".
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Adding the overlay to your project is relatively simple. First,
|
||||
add the "hapi-fhir-testpage-overlay" dependency to the dependencies
|
||||
section of your POM.xml file.
|
||||
|
||||
<source><![CDATA[<dependencies>
|
||||
<!-- ... other dependencies ... -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>]]></source>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Then, add the following WAR plugin to the plugins section
|
||||
of your POM.xml
|
||||
|
||||
<source><![CDATA[<build>
|
||||
<plugins>
|
||||
<!-- ... other plugins ... -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<overlays>
|
||||
<overlay>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
</overlay>
|
||||
</overlays>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</build>
|
||||
</plugins>]]></source>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Next, create the following directory in your project
|
||||
if it doesn't already exist:<br/>
|
||||
<code>src/main/webapp/WEB-INF</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Then, create a file in that directory
|
||||
called <code>hapi-fhir-tester-config.xml</code>
|
||||
and copy in the following contents:
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="file" value="restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Note that the URL in the file above must be customized to point to
|
||||
the FHIR endpoint your server will be deployed to. For example, if you
|
||||
are naming your project "myfhir-1.0.war" and your endpoint in the WAR is
|
||||
deployed to "/fhirbase/*" then you should put a URL similar to
|
||||
<code>http://localhost:8080/myfhir-1.0/fhirbase</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Finally, in the same directory you should open you web.xml file. This file is
|
||||
required in order to deploy to a servlet container and you should create it if
|
||||
it does not already exist. Place the following contents in that file.
|
||||
</p>
|
||||
|
||||
<source><![CDATA[<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
/WEB-INF/hapi-fhir-tester-application-context.xml
|
||||
/WEB-INF/hapi-fhir-tester-config.xml
|
||||
</param-value>
|
||||
</context-param>
|
||||
<servlet>
|
||||
<servlet-name>spring</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
/WEB-INF/hapi-fhir-tester-application-context.xml
|
||||
/WEB-INF/hapi-fhir-tester-config.xml
|
||||
</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>2</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>spring</servlet-name>
|
||||
<url-pattern>/tester/*</url-pattern>
|
||||
</servlet-mapping>]]></source>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Customizing the Overlay">
|
||||
|
||||
<p>
|
||||
The most important customization required is to
|
||||
set the FHIR server base URL in the
|
||||
<code>hapi-fhir-tester-config.xml</code>
|
||||
configuration file created during the
|
||||
previous step.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Beyond this, the entire tester application is built
|
||||
from a number of
|
||||
<a href="http://thymeleaf.org">Thymeleaf</a>
|
||||
template files, any of which can be replaced to
|
||||
create your own look and feel. All of the templates
|
||||
can be found in your built war (after running the Maven
|
||||
build), or in the target directory's staging area, in
|
||||
<code>WEB-INF/templates</code>. By placing a file
|
||||
with the same path/name in your <code>src/main/webapp/WEB-INF/templates</code>
|
||||
directory you can replace the built in template
|
||||
with your own file.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="A Complete Example">
|
||||
|
||||
<p>
|
||||
The "Restful Server Example" project contains a complete working
|
||||
example of the FHIR Tester as a part of its configuration. You may
|
||||
wish to browse its source to see how this works:<br/>
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/restful-server-example">https://github.com/jamesagnew/hapi-fhir/tree/master/restful-server-example</a>
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
Loading…
Reference in New Issue