Let server return profile info if configured to do so
This commit is contained in:
parent
731d369be0
commit
ab17c9f3d1
|
@ -629,8 +629,6 @@ class ModelScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String profile = resourceDefinition.profile();
|
|
||||||
|
|
||||||
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(resourceName, theClass, resourceDefinition);
|
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(resourceName, theClass, resourceDefinition);
|
||||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||||
if (primaryNameProvider) {
|
if (primaryNameProvider) {
|
||||||
|
|
|
@ -352,4 +352,8 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStandardProfile() {
|
||||||
|
return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,7 @@ public abstract class BaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
|
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
|
||||||
|
OperationOutcome oo=null;
|
||||||
if (Constants.CT_TEXT.equals(mimeType)) {
|
if (Constants.CT_TEXT.equals(mimeType)) {
|
||||||
message = message + ": " + body;
|
message = message + ": " + body;
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,7 +219,7 @@ public abstract class BaseClient {
|
||||||
if (enc != null) {
|
if (enc != null) {
|
||||||
IParser p = enc.newParser(theContext);
|
IParser p = enc.newParser(theContext);
|
||||||
try {
|
try {
|
||||||
OperationOutcome oo = p.parseResource(OperationOutcome.class, body);
|
oo = p.parseResource(OperationOutcome.class, body);
|
||||||
if (oo.getIssueFirstRep().getDetails().isEmpty()==false) {
|
if (oo.getIssueFirstRep().getDetails().isEmpty()==false) {
|
||||||
message = message + ": " + oo.getIssueFirstRep().getDetails().getValue();
|
message = message + ": " + oo.getIssueFirstRep().getDetails().getValue();
|
||||||
}
|
}
|
||||||
|
@ -231,6 +232,7 @@ public abstract class BaseClient {
|
||||||
keepResponseAndLogIt(theLogRequestAndResponse, response, body);
|
keepResponseAndLogIt(theLogRequestAndResponse, response, body);
|
||||||
|
|
||||||
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
|
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
|
||||||
|
exception.setOperationOutcome(oo);
|
||||||
|
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
exception.setResponseBody(body);
|
exception.setResponseBody(body);
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESTful server behaviour for automatically adding profile tags
|
||||||
|
*
|
||||||
|
* @see RestfulServer#setAddProfileTag(AddProfileTagEnum)
|
||||||
|
*/
|
||||||
|
enum AddProfileTagEnum {
|
||||||
|
/**
|
||||||
|
* Do not add profile tags automatically
|
||||||
|
*/
|
||||||
|
NEVER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add any profile tags that returned resources appear to conform to
|
||||||
|
*/
|
||||||
|
ALWAYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add any profile tags that returned resources appear to conform to if the resource is a non-standard class (e.g.
|
||||||
|
* it is an instance of a class that extends a built in type, but adds or constrains it)
|
||||||
|
*/
|
||||||
|
ONLY_FOR_CUSTOM
|
||||||
|
}
|
|
@ -55,7 +55,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
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.IResource;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
@ -87,6 +86,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private AddProfileTagEnum myAddProfileTag;
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
private String myImplementationDescription;
|
private String myImplementationDescription;
|
||||||
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
||||||
|
@ -119,42 +119,316 @@ public class RestfulServer extends HttpServlet {
|
||||||
/**
|
/**
|
||||||
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||||
* <p>
|
* <p>
|
||||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling functionality.
|
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||||
|
* inadvertantly disabling functionality.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||||
theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the setting for automatically adding profile tags
|
||||||
|
*
|
||||||
|
* @see #setAddProfileTag(AddProfileTagEnum)
|
||||||
|
*/
|
||||||
|
public AddProfileTagEnum getAddProfileTag() {
|
||||||
|
return myAddProfileTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||||
|
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||||
|
*/
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return myFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImplementationDescription() {
|
||||||
|
return myImplementationDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return myPagingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the non-resource specific providers which implement method calls on this server
|
||||||
|
*
|
||||||
|
* @see #getResourceProviders()
|
||||||
|
*/
|
||||||
|
public Collection<Object> getPlainProviders() {
|
||||||
|
return myPlainProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ResourceBinding> getResourceBindings() {
|
||||||
|
return myResourceNameToProvider.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the resource providers for this server
|
||||||
|
*/
|
||||||
|
public Collection<IResourceProvider> getResourceProviders() {
|
||||||
|
return myResourceProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the security manager, or <code>null</code> if none
|
||||||
|
*/
|
||||||
|
public ISecurityManager getSecurityManager() {
|
||||||
|
return mySecurityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||||
|
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||||
|
*/
|
||||||
|
public IServerAddressStrategy getServerAddressStrategy() {
|
||||||
|
return myServerAddressStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||||
|
* (metadata) statement.
|
||||||
|
* <p>
|
||||||
|
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||||
|
* if you do not wish to export a conformance statement.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Object getServerConformanceProvider() {
|
||||||
|
return myServerConformanceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||||
|
* but can be helpful to set with something appropriate.
|
||||||
|
*
|
||||||
|
* @see RestfulServer#setServerName(String)
|
||||||
|
*/
|
||||||
|
public String getServerName() {
|
||||||
|
return myServerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceProvider getServerProfilesProvider() {
|
||||||
|
return new ServerProfileProvider(getFhirContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||||
|
* only, but can be helpful to set with something appropriate.
|
||||||
|
*/
|
||||||
|
public String getServerVersion() {
|
||||||
|
return myServerVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||||
|
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||||
|
* initialization of the restful server's internal init.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final void init() throws ServletException {
|
||||||
|
initialize();
|
||||||
|
try {
|
||||||
|
ourLog.info("Initializing HAPI FHIR restful server");
|
||||||
|
|
||||||
|
mySecurityManager = getSecurityManager();
|
||||||
|
if (null == mySecurityManager) {
|
||||||
|
ourLog.trace("No security manager has been provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||||
|
if (resourceProvider != null) {
|
||||||
|
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||||
|
for (IResourceProvider nextProvider : resourceProvider) {
|
||||||
|
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
||||||
|
if (resourceType == null) {
|
||||||
|
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
||||||
|
}
|
||||||
|
if (typeToProvider.containsKey(resourceType)) {
|
||||||
|
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||||
|
}
|
||||||
|
typeToProvider.put(resourceType, nextProvider);
|
||||||
|
}
|
||||||
|
ourLog.info("Got {} resource providers", typeToProvider.size());
|
||||||
|
for (IResourceProvider provider : typeToProvider.values()) {
|
||||||
|
assertProviderIsValid(provider);
|
||||||
|
findResourceMethods(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Object> providers = getPlainProviders();
|
||||||
|
if (providers != null) {
|
||||||
|
for (Object next : providers) {
|
||||||
|
assertProviderIsValid(next);
|
||||||
|
findResourceMethods(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findResourceMethods(getServerProfilesProvider());
|
||||||
|
findSystemMethods(getServerConformanceProvider());
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||||
|
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
myStarted = true;
|
||||||
|
ourLog.info("A FHIR has been lit on this server");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
|
return myUseBrowserFriendlyContentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER}
|
||||||
|
* (which is the default), the server will automatically add a profile tag based on the class of the resource(s)
|
||||||
|
* being returned.
|
||||||
|
*
|
||||||
|
* @param theAddProfileTag
|
||||||
|
* The behaviour enum (must not be null)
|
||||||
|
*/
|
||||||
|
public void setAddProfileTag(AddProfileTagEnum theAddProfileTag) {
|
||||||
|
Validate.notNull(theAddProfileTag, "theAddProfileTag must not be null");
|
||||||
|
myAddProfileTag = theAddProfileTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFhirContext(FhirContext theFhirContext) {
|
||||||
|
Validate.notNull(theFhirContext, "FhirContext must not be null");
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplementationDescription(String theImplementationDescription) {
|
||||||
|
myImplementationDescription = theImplementationDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
|
||||||
|
*/
|
||||||
|
public void setPagingProvider(IPagingProvider thePagingProvider) {
|
||||||
|
myPagingProvider = thePagingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the non-resource specific providers which implement method calls on this server.
|
||||||
|
*
|
||||||
|
* @see #setResourceProviders(Collection)
|
||||||
|
*/
|
||||||
|
public void setPlainProviders(Collection<Object> theProviders) {
|
||||||
|
myPlainProviders = theProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the non-resource specific providers which implement method calls on this server.
|
||||||
|
*
|
||||||
|
* @see #setResourceProviders(Collection)
|
||||||
|
*/
|
||||||
|
public void setPlainProviders(Object... theProv) {
|
||||||
|
setPlainProviders(Arrays.asList(theProv));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the non-resource specific providers which implement method calls on this server
|
||||||
|
*
|
||||||
|
* @see #setResourceProviders(Collection)
|
||||||
|
*/
|
||||||
|
public void setProviders(Object... theProviders) {
|
||||||
|
myPlainProviders = Arrays.asList(theProviders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resource providers for this server
|
||||||
|
*/
|
||||||
|
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
||||||
|
myResourceProviders = theResourceProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the resource providers for this server
|
||||||
|
*/
|
||||||
|
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
||||||
|
myResourceProviders = Arrays.asList(theResourceProviders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the security manager, or <code>null</code> if none
|
||||||
|
*/
|
||||||
|
public void setSecurityManager(ISecurityManager theSecurityManager) {
|
||||||
|
mySecurityManager = theSecurityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||||
|
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||||
|
*/
|
||||||
|
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||||
|
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||||
|
myServerAddressStrategy = theServerAddressStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||||
|
* (metadata) statement.
|
||||||
|
* <p>
|
||||||
|
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||||
|
* if you do not wish to export a conformance statement.
|
||||||
|
* </p>
|
||||||
|
* Note that this method can only be called before the server is initialized.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||||
|
* {@link IllegalStateException} if called after that.
|
||||||
|
*/
|
||||||
|
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||||
|
if (myStarted) {
|
||||||
|
throw new IllegalStateException("Server is already started");
|
||||||
|
}
|
||||||
|
myServerConformanceProvider = theServerConformanceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||||
|
* but can be helpful to set with something appropriate.
|
||||||
|
*/
|
||||||
|
public void setServerName(String theServerName) {
|
||||||
|
myServerName = theServerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||||
|
* only, but can be helpful to set with something appropriate.
|
||||||
|
*/
|
||||||
|
public void setServerVersion(String theServerVersion) {
|
||||||
|
myServerVersion = theServerVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||||
|
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||||
|
*/
|
||||||
|
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||||
|
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
||||||
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
||||||
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Class ust be public");
|
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Class ust be public");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
|
||||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
*/
|
||||||
|
private int escapedLength(String theServletPath) {
|
||||||
|
int delta = 0;
|
||||||
|
for (int i = 0; i < theServletPath.length(); i++) {
|
||||||
|
char next = theServletPath.charAt(i);
|
||||||
|
if (next == ' ') {
|
||||||
|
delta = delta + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
|
||||||
}
|
}
|
||||||
|
return theServletPath.length() + delta;
|
||||||
@Override
|
|
||||||
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
|
||||||
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findResourceMethods(Object theProvider) throws Exception {
|
private void findResourceMethods(Object theProvider) throws Exception {
|
||||||
|
@ -176,6 +450,23 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
||||||
|
// serializing responses from this server, or <code>null</code> (which is
|
||||||
|
// the default) to disable narrative generation.
|
||||||
|
// * Note that this method can only be called before the server is
|
||||||
|
// initialized.
|
||||||
|
// *
|
||||||
|
// * @throws IllegalStateException
|
||||||
|
// * Note that this method can only be called prior to {@link #init()
|
||||||
|
// initialization} and will throw an {@link IllegalStateException} if called
|
||||||
|
// after that.
|
||||||
|
// */
|
||||||
|
// public void setNarrativeGenerator(INarrativeGenerator
|
||||||
|
// theNarrativeGenerator) {
|
||||||
|
// myNarrativeGenerator = theNarrativeGenerator;
|
||||||
|
// }
|
||||||
|
|
||||||
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
|
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
@ -249,86 +540,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
|
||||||
* creating their own.
|
|
||||||
*/
|
|
||||||
public FhirContext getFhirContext() {
|
|
||||||
return myFhirContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getImplementationDescription() {
|
|
||||||
return myImplementationDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPagingProvider getPagingProvider() {
|
|
||||||
return myPagingProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the non-resource specific providers which implement method calls on this server
|
|
||||||
*
|
|
||||||
* @see #getResourceProviders()
|
|
||||||
*/
|
|
||||||
public Collection<Object> getPlainProviders() {
|
|
||||||
return myPlainProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceBinding> getResourceBindings() {
|
|
||||||
return myResourceNameToProvider.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the resource providers for this server
|
|
||||||
*/
|
|
||||||
public Collection<IResourceProvider> getResourceProviders() {
|
|
||||||
return myResourceProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the security manager, or <code>null</code> if none
|
|
||||||
*/
|
|
||||||
public ISecurityManager getSecurityManager() {
|
|
||||||
return mySecurityManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
|
||||||
*/
|
|
||||||
public IServerAddressStrategy getServerAddressStrategy() {
|
|
||||||
return myServerAddressStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
|
||||||
* <p>
|
|
||||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public Object getServerConformanceProvider() {
|
|
||||||
return myServerConformanceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
|
||||||
*
|
|
||||||
* @see RestfulServer#setServerName(String)
|
|
||||||
*/
|
|
||||||
public String getServerName() {
|
|
||||||
return myServerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IResourceProvider getServerProfilesProvider() {
|
|
||||||
return new ServerProfileProvider(getFhirContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
|
||||||
*/
|
|
||||||
public String getServerVersion() {
|
|
||||||
return myServerVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePagingRequest(Request theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
private void handlePagingRequest(Request theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
||||||
IBundleProvider resultList = getPagingProvider().retrieveResultList(thePagingAction);
|
IBundleProvider resultList = getPagingProvider().retrieveResultList(thePagingAction);
|
||||||
if (resultList == null) {
|
if (resultList == null) {
|
||||||
|
@ -361,11 +572,40 @@ public class RestfulServer extends HttpServlet {
|
||||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||||
boolean respondGzip = theRequest.isRespondGzip();
|
boolean respondGzip = theRequest.isRespondGzip();
|
||||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start,
|
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, respondGzip);
|
||||||
count, thePagingAction, respondGzip);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean requestIsBrowser(HttpServletRequest theRequest) {
|
||||||
|
String userAgent = theRequest.getHeader("User-Agent");
|
||||||
|
return userAgent != null && userAgent.contains("Mozilla");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||||
String fhirServerBase = null;
|
String fhirServerBase = null;
|
||||||
try {
|
try {
|
||||||
|
@ -547,7 +787,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
||||||
OperationOutcome oo=null;
|
OperationOutcome oo = null;
|
||||||
int statusCode = 500;
|
int statusCode = 500;
|
||||||
|
|
||||||
if (e instanceof BaseServerResponseException) {
|
if (e instanceof BaseServerResponseException) {
|
||||||
|
@ -586,218 +826,13 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count length of URL string, but treating unescaped sequences (e.g. ' ') as their unescaped equivalent (%20)
|
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||||
*/
|
* server being used.
|
||||||
private int escapedLength(String theServletPath) {
|
|
||||||
int delta = 0;
|
|
||||||
for (int i = 0; i < theServletPath.length(); i++) {
|
|
||||||
char next = theServletPath.charAt(i);
|
|
||||||
if (next == ' ') {
|
|
||||||
delta = delta + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return theServletPath.length() + delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
|
||||||
* called immediately before beginning initialization of the restful server's internal init.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public final void init() throws ServletException {
|
|
||||||
initialize();
|
|
||||||
try {
|
|
||||||
ourLog.info("Initializing HAPI FHIR restful server");
|
|
||||||
|
|
||||||
mySecurityManager = getSecurityManager();
|
|
||||||
if (null == mySecurityManager) {
|
|
||||||
ourLog.trace("No security manager has been provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
|
||||||
if (resourceProvider != null) {
|
|
||||||
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
|
||||||
for (IResourceProvider nextProvider : resourceProvider) {
|
|
||||||
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
|
||||||
if (resourceType == null) {
|
|
||||||
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
|
||||||
}
|
|
||||||
if (typeToProvider.containsKey(resourceType)) {
|
|
||||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
|
||||||
}
|
|
||||||
typeToProvider.put(resourceType, nextProvider);
|
|
||||||
}
|
|
||||||
ourLog.info("Got {} resource providers", typeToProvider.size());
|
|
||||||
for (IResourceProvider provider : typeToProvider.values()) {
|
|
||||||
assertProviderIsValid(provider);
|
|
||||||
findResourceMethods(provider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<Object> providers = getPlainProviders();
|
|
||||||
if (providers != null) {
|
|
||||||
for (Object next : providers) {
|
|
||||||
assertProviderIsValid(next);
|
|
||||||
findResourceMethods(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findResourceMethods(getServerProfilesProvider());
|
|
||||||
findSystemMethods(getServerConformanceProvider());
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
|
||||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
myStarted = true;
|
|
||||||
ourLog.info("A FHIR has been lit on this server");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
|
||||||
*/
|
*/
|
||||||
protected void initialize() throws ServletException {
|
protected void initialize() throws ServletException {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUseBrowserFriendlyContentTypes() {
|
|
||||||
return myUseBrowserFriendlyContentTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean requestIsBrowser(HttpServletRequest theRequest) {
|
|
||||||
String userAgent = theRequest.getHeader("User-Agent");
|
|
||||||
return userAgent != null && userAgent.contains("Mozilla");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFhirContext(FhirContext theFhirContext) {
|
|
||||||
Validate.notNull(theFhirContext, "FhirContext must not be null");
|
|
||||||
myFhirContext = theFhirContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImplementationDescription(String theImplementationDescription) {
|
|
||||||
myImplementationDescription = theImplementationDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the paging provider to use, or <code>null</code> to use no paging (which is the default)
|
|
||||||
*/
|
|
||||||
public void setPagingProvider(IPagingProvider thePagingProvider) {
|
|
||||||
myPagingProvider = thePagingProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
|
||||||
// serializing responses from this server, or <code>null</code> (which is
|
|
||||||
// the default) to disable narrative generation.
|
|
||||||
// * Note that this method can only be called before the server is
|
|
||||||
// initialized.
|
|
||||||
// *
|
|
||||||
// * @throws IllegalStateException
|
|
||||||
// * Note that this method can only be called prior to {@link #init()
|
|
||||||
// initialization} and will throw an {@link IllegalStateException} if called
|
|
||||||
// after that.
|
|
||||||
// */
|
|
||||||
// public void setNarrativeGenerator(INarrativeGenerator
|
|
||||||
// theNarrativeGenerator) {
|
|
||||||
// myNarrativeGenerator = theNarrativeGenerator;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the non-resource specific providers which implement method calls on this server.
|
|
||||||
*
|
|
||||||
* @see #setResourceProviders(Collection)
|
|
||||||
*/
|
|
||||||
public void setPlainProviders(Collection<Object> theProviders) {
|
|
||||||
myPlainProviders = theProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the non-resource specific providers which implement method calls on this server.
|
|
||||||
*
|
|
||||||
* @see #setResourceProviders(Collection)
|
|
||||||
*/
|
|
||||||
public void setPlainProviders(Object... theProv) {
|
|
||||||
setPlainProviders(Arrays.asList(theProv));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the non-resource specific providers which implement method calls on this server
|
|
||||||
*
|
|
||||||
* @see #setResourceProviders(Collection)
|
|
||||||
*/
|
|
||||||
public void setProviders(Object... theProviders) {
|
|
||||||
myPlainProviders = Arrays.asList(theProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the resource providers for this server
|
|
||||||
*/
|
|
||||||
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
|
||||||
myResourceProviders = theResourceProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the resource providers for this server
|
|
||||||
*/
|
|
||||||
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
|
||||||
myResourceProviders = Arrays.asList(theResourceProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the security manager, or <code>null</code> if none
|
|
||||||
*/
|
|
||||||
public void setSecurityManager(ISecurityManager theSecurityManager) {
|
|
||||||
mySecurityManager = theSecurityManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
|
||||||
*/
|
|
||||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
|
||||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
|
||||||
myServerAddressStrategy = theServerAddressStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
|
||||||
* <p>
|
|
||||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
|
||||||
* </p>
|
|
||||||
* Note that this method can only be called before the server is initialized.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException
|
|
||||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
|
||||||
*/
|
|
||||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
|
||||||
if (myStarted) {
|
|
||||||
throw new IllegalStateException("Server is already started");
|
|
||||||
}
|
|
||||||
myServerConformanceProvider = theServerConformanceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
|
||||||
*/
|
|
||||||
public void setServerName(String theServerName) {
|
|
||||||
myServerName = theServerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
|
||||||
*/
|
|
||||||
public void setServerVersion(String theServerVersion) {
|
|
||||||
myServerVersion = theServerVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
|
||||||
* instead of a FHIR
|
|
||||||
*/
|
|
||||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
|
||||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults) {
|
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.getAuthorName().setValue(theAuthor);
|
bundle.getAuthorName().setValue(theAuthor);
|
||||||
|
@ -857,7 +892,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
} while (references.isEmpty() == false);
|
} while (references.isEmpty() == false);
|
||||||
|
|
||||||
bundle.addResource(next, theContext, theServerBase);
|
bundle.addResource(next, theContext, theServerBase);
|
||||||
// addProfileToBundleEntry(theContext, next, entry);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,26 +900,12 @@ public class RestfulServer extends HttpServlet {
|
||||||
*/
|
*/
|
||||||
for (IResource next : addedResources) {
|
for (IResource next : addedResources) {
|
||||||
bundle.addResource(next, theContext, theServerBase);
|
bundle.addResource(next, theContext, theServerBase);
|
||||||
// addProfileToBundleEntry(theContext, next, entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle.getTotalResults().setValue(theTotalResults);
|
bundle.getTotalResults().setValue(theTotalResults);
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource next, BundleEntry entry) {
|
|
||||||
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
|
||||||
if (profileTags.isEmpty()) {
|
|
||||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(next);
|
|
||||||
String profile = nextDef.getResourceProfile();
|
|
||||||
if (isNotBlank(profile)) {
|
|
||||||
entry.addCategory(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(theServerBase);
|
b.append(theServerBase);
|
||||||
|
@ -1012,17 +1032,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
|
||||||
Writer writer;
|
|
||||||
if (theRespondGzip) {
|
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
|
||||||
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
|
||||||
} else {
|
|
||||||
writer = theHttpResponse.getWriter();
|
|
||||||
}
|
|
||||||
return writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean prettyPrintResponse(Request theRequest) {
|
public static boolean prettyPrintResponse(Request theRequest) {
|
||||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||||
|
@ -1048,9 +1057,8 @@ public class RestfulServer extends HttpServlet {
|
||||||
return prettyPrint;
|
return prettyPrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip)
|
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip) throws IOException {
|
||||||
throws IOException {
|
|
||||||
assert !theServerBase.endsWith("/");
|
assert !theServerBase.endsWith("/");
|
||||||
|
|
||||||
theHttpResponse.setStatus(200);
|
theHttpResponse.setStatus(200);
|
||||||
|
@ -1102,6 +1110,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||||
|
for (int i = 0; i < resourceList.size(); i++) {
|
||||||
|
IResource nextRes = resourceList.get(i);
|
||||||
|
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||||
|
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||||
|
addProfileToBundleEntry(theServer.getFhirContext(), nextRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Bundle bundle = createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size());
|
Bundle bundle = createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size());
|
||||||
|
|
||||||
bundle.setPublished(theResult.getPublished());
|
bundle.setPublished(theResult.getPublished());
|
||||||
|
@ -1137,14 +1155,40 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip,
|
||||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException {
|
String theServerBase) throws IOException {
|
||||||
int stausCode = 200;
|
int stausCode = 200;
|
||||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource) {
|
||||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase) throws IOException {
|
|
||||||
|
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||||
|
if (tl == null) {
|
||||||
|
tl = new TagList();
|
||||||
|
ResourceMetadataKeyEnum.TAG_LIST.put(theResource, tl);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
|
||||||
|
String profile = nextDef.getResourceProfile();
|
||||||
|
if (isNotBlank(profile)) {
|
||||||
|
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
||||||
|
Writer writer;
|
||||||
|
if (theRespondGzip) {
|
||||||
|
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||||
|
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
||||||
|
} else {
|
||||||
|
writer = theHttpResponse.getWriter();
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode,
|
||||||
|
boolean theRespondGzip, String theServerBase) throws IOException {
|
||||||
theHttpResponse.setStatus(stausCode);
|
theHttpResponse.setStatus(stausCode);
|
||||||
|
|
||||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||||
|
@ -1153,6 +1197,13 @@ public class RestfulServer extends HttpServlet {
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId);
|
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||||
|
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
|
||||||
|
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||||
|
addProfileToBundleEntry(theServer.getFhirContext(), theResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (theResource instanceof Binary) {
|
if (theResource instanceof Binary) {
|
||||||
Binary bin = (Binary) theResource;
|
Binary bin = (Binary) theResource;
|
||||||
if (isNotBlank(bin.getContentType())) {
|
if (isNotBlank(bin.getContentType())) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
|
@ -432,7 +433,7 @@ public class JsonParserTest {
|
||||||
|
|
||||||
ourLog.info(str);
|
ourLog.info(str);
|
||||||
assertThat(str, StringContains.containsString("<div>AAA</div>"));
|
assertThat(str, StringContains.containsString("<div>AAA</div>"));
|
||||||
String substring = "\"resource\":\"#";
|
String substring = "\"reference\":\"#";
|
||||||
assertThat(str, StringContains.containsString(substring));
|
assertThat(str, StringContains.containsString(substring));
|
||||||
|
|
||||||
int idx = str.indexOf(substring) + substring.length();
|
int idx = str.indexOf(substring) + substring.length();
|
||||||
|
@ -473,7 +474,7 @@ public class JsonParserTest {
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
String val = parser.encodeResourceToString(patient);
|
||||||
ourLog.info(val);
|
ourLog.info(val);
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"reference\":\"Organization/123\"}}]"));
|
||||||
|
|
||||||
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
|
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
@ -526,7 +527,7 @@ public class JsonParserTest {
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
String val = parser.encodeResourceToString(patient);
|
||||||
ourLog.info(val);
|
ourLog.info(val);
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"reference\":\"Organization/123\"}}]"));
|
||||||
|
|
||||||
Patient actual = parser.parseResource(Patient.class, val);
|
Patient actual = parser.parseResource(Patient.class, val);
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
@ -907,6 +908,18 @@ public class JsonParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HAPI FHIR < 0.6 incorrectly used "resource" instead of "reference"
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseWithIncorrectReference() throws IOException {
|
||||||
|
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
||||||
|
jsonString = jsonString.replace("\"reference\"", "\"resource\"");
|
||||||
|
Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class,jsonString);
|
||||||
|
assertEquals("Organization/1", parsed.getManagingOrganization().getReference().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleResourceEncodeWithCustomType() throws IOException {
|
public void testSimpleResourceEncodeWithCustomType() throws IOException {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -27,6 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
@ -138,17 +138,48 @@ public class ExceptionHandlingTest {
|
||||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
client.read(Patient.class, new IdDt("Patient/1234"));
|
client.read(Patient.class, new IdDt("Patient/1234"));
|
||||||
fail();
|
fail();
|
||||||
} catch (InternalErrorException e) {
|
} catch (InternalErrorException e) {
|
||||||
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
|
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
|
||||||
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
|
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
|
||||||
|
assertNotNull(e.getOperationOutcome());
|
||||||
|
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetails().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFail500WithOperationOutcomeMessageGeneric() throws Exception {
|
||||||
|
OperationOutcome oo = new OperationOutcome();
|
||||||
|
oo.getIssueFirstRep().getDetails().setValue("Help I'm a bug");
|
||||||
|
String msg = myCtx.newJsonParser().encodeResourceToString(oo);
|
||||||
|
String contentType = Constants.CT_FHIR_JSON;
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, "Internal Error"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", contentType + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IMyClient client = myCtx.newRestfulClient(IMyClient.class,"http://example.com/fhir");
|
||||||
|
try {
|
||||||
|
client.read(new IdDt("Patient/1234"));
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertThat(e.getMessage(), StringContains.containsString("HTTP 500 Internal Error"));
|
||||||
|
assertThat(e.getMessage(), StringContains.containsString("Help I'm a bug"));
|
||||||
|
assertNotNull(e.getOperationOutcome());
|
||||||
|
assertEquals("Help I'm a bug",e.getOperationOutcome().getIssueFirstRep().getDetails().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IMyClient extends IRestfulClient
|
||||||
|
{
|
||||||
|
@Read
|
||||||
|
Patient read(@IdParam IdDt theId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -8,14 +8,10 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.NameValuePair;
|
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
import org.apache.http.message.BasicNameValuePair;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
@ -29,16 +25,11 @@ import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
|
||||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
|
||||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,10 +41,14 @@ public class CustomTypeTest {
|
||||||
private static FhirContext ourCtx = new FhirContext(ExtendedPatient.class);
|
private static FhirContext ourCtx = new FhirContext(ExtendedPatient.class);
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
private static RestfulServer ourServlet;
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchReturnsProfile() throws Exception {
|
public void testSearchReturnsProfile() throws Exception {
|
||||||
|
ourServlet.setAddProfileTag(AddProfileTagEnum.ONLY_FOR_CUSTOM);
|
||||||
|
ourReturnExtended=true;
|
||||||
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
@ -72,7 +67,65 @@ public class CustomTypeTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchReturnsNoProfileForNormalType() throws Exception {
|
||||||
|
ourServlet.setAddProfileTag(AddProfileTagEnum.ONLY_FOR_CUSTOM);
|
||||||
|
ourReturnExtended=false;
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
BundleEntry entry = bundle.getEntries().get(0);
|
||||||
|
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||||
|
assertEquals(0, profileTags.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchReturnsNoProfileForExtendedType() throws Exception {
|
||||||
|
ourServlet.setAddProfileTag(AddProfileTagEnum.NEVER);
|
||||||
|
ourReturnExtended=true;
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
BundleEntry entry = bundle.getEntries().get(0);
|
||||||
|
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||||
|
assertEquals(0, profileTags.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchReturnsProfileForNormalType() throws Exception {
|
||||||
|
ourServlet.setAddProfileTag(AddProfileTagEnum.ALWAYS);
|
||||||
|
ourReturnExtended=false;
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
BundleEntry entry = bundle.getEntries().get(0);
|
||||||
|
List<Tag> profileTags = entry.getCategories().getTagsWithScheme(Tag.HL7_ORG_PROFILE_TAG);
|
||||||
|
assertEquals(1, profileTags.size());
|
||||||
|
assertEquals("http://hl7.org/fhir/profiles/Patient", profileTags.get(0).getTerm());
|
||||||
|
|
||||||
|
Patient p = (Patient) bundle.getEntries().get(0).getResource();
|
||||||
|
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
|
@ -87,11 +140,11 @@ public class CustomTypeTest {
|
||||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||||
|
|
||||||
ServletHandler proxyHandler = new ServletHandler();
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
RestfulServer servlet = new RestfulServer();
|
ourServlet = new RestfulServer();
|
||||||
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
|
|
||||||
servlet.setResourceProviders(patientProvider);
|
ourServlet.setResourceProviders(patientProvider);
|
||||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
ourServer.setHandler(proxyHandler);
|
ourServer.setHandler(proxyHandler);
|
||||||
ourServer.start();
|
ourServer.start();
|
||||||
|
@ -111,6 +164,8 @@ public class CustomTypeTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean ourReturnExtended = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
|
@ -120,7 +175,7 @@ public class CustomTypeTest {
|
||||||
public List<Patient> findPatient(@OptionalParam(name = "_id") StringParam theParam) {
|
public List<Patient> findPatient(@OptionalParam(name = "_id") StringParam theParam) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
ExtendedPatient patient = new ExtendedPatient();
|
Patient patient = ourReturnExtended ? new ExtendedPatient() : new Patient();
|
||||||
patient.setId("1");
|
patient.setId("1");
|
||||||
patient.addIdentifier("system", "identifier123");
|
patient.addIdentifier("system", "identifier123");
|
||||||
if (theParam != null) {
|
if (theParam != null) {
|
||||||
|
|
|
@ -143,306 +143,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"managingOrganization":{
|
"managingOrganization":{
|
||||||
"resource":"Organization/1"
|
"reference":"Organization/1"
|
||||||
},
|
|
||||||
"active":true
|
|
||||||
}13:16:59.119 [main] INFO ca.uhn.fhir.parser.JsonParserTest -
|
|
||||||
{
|
|
||||||
"resourceType":"Patient",
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:att",
|
|
||||||
"valueAttachment":{
|
|
||||||
"contentType":"aaaa",
|
|
||||||
"data":"AAAA"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext",
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext:1",
|
|
||||||
"valuestring":"str1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext:2",
|
|
||||||
"valuestring":"str2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modifierExtension":[
|
|
||||||
{
|
|
||||||
"url":"urn:modext",
|
|
||||||
"valuedate":"2011-01-02"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"text":{
|
|
||||||
"status":"generated",
|
|
||||||
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n <table>\n <tbody>\n <tr>\n <td>Name</td>\n <td>Peter James <b>Chalmers</b> (\"Jim\")</td>\n </tr>\n <tr>\n <td>Address</td>\n <td>534 Erewhon, Pleasantville, Vic, 3999</td>\n </tr>\n <tr>\n <td>Contacts</td>\n <td>Home: unknown. Work: (03) 5555 6473</td>\n </tr>\n <tr>\n <td>Id</td>\n <td>MRN: 12345 (Acme Healthcare)</td>\n </tr>\n </tbody>\n </table>\n </div>"
|
|
||||||
},
|
|
||||||
"identifier":[
|
|
||||||
{
|
|
||||||
"use":"usual",
|
|
||||||
"label":"MRN",
|
|
||||||
"system":"urn:oid:1.2.36.146.595.217.0.1",
|
|
||||||
"value":"12345",
|
|
||||||
"period":{
|
|
||||||
"start":"2001-05-06"
|
|
||||||
},
|
|
||||||
"assigner":{
|
|
||||||
"display":"Acme Healthcare"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name":[
|
|
||||||
{
|
|
||||||
"use":"official",
|
|
||||||
"family":[
|
|
||||||
"Chalmers"
|
|
||||||
],
|
|
||||||
"given":[
|
|
||||||
"Peter",
|
|
||||||
"James"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"use":"usual",
|
|
||||||
"given":[
|
|
||||||
"Jim"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"telecom":[
|
|
||||||
{
|
|
||||||
"use":"home"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"system":"phone",
|
|
||||||
"value":"(03) 5555 6473",
|
|
||||||
"use":"work"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gender":{
|
|
||||||
"coding":[
|
|
||||||
{
|
|
||||||
"system":"http://hl7.org/fhir/v3/AdministrativeGender",
|
|
||||||
"code":"M",
|
|
||||||
"display":"Male"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"birthDate":"1974-12-25",
|
|
||||||
"deceasedBoolean":false,
|
|
||||||
"address":[
|
|
||||||
{
|
|
||||||
"use":"home",
|
|
||||||
"line":[
|
|
||||||
"534 Erewhon St"
|
|
||||||
],
|
|
||||||
"city":"PleasantVille",
|
|
||||||
"state":"Vic",
|
|
||||||
"zip":"3999"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"use":"old",
|
|
||||||
"line":[
|
|
||||||
"SecondAddress"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contact":[
|
|
||||||
{
|
|
||||||
"relationship":[
|
|
||||||
{
|
|
||||||
"coding":[
|
|
||||||
{
|
|
||||||
"system":"http://hl7.org/fhir/patient-contact-relationship",
|
|
||||||
"code":"partner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name":{
|
|
||||||
"family":[
|
|
||||||
"du",
|
|
||||||
"March??"
|
|
||||||
],
|
|
||||||
"_family":[
|
|
||||||
{
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"http://hl7.org/fhir/Profile/iso-21090#qualifier",
|
|
||||||
"valuecode":"VV"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"given":[
|
|
||||||
"B??n??dicte"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"telecom":[
|
|
||||||
{
|
|
||||||
"system":"phone",
|
|
||||||
"value":"+33 (237) 998327"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"managingOrganization":{
|
|
||||||
"resource":"Organization/1"
|
|
||||||
},
|
|
||||||
"active":true
|
|
||||||
}
|
|
||||||
13:16:59.122 [main] INFO ca.uhn.fhir.parser.JsonParserTest -
|
|
||||||
{
|
|
||||||
"resourceType":"Patient",
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:att",
|
|
||||||
"valueAttachment":{
|
|
||||||
"contentType":"aaaa",
|
|
||||||
"data":"AAAA"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext",
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext:1",
|
|
||||||
"valuestring":"str1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url":"urn:patientext:moreext:2",
|
|
||||||
"valuestring":"str2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modifierExtension":[
|
|
||||||
{
|
|
||||||
"url":"urn:modext",
|
|
||||||
"valuedate":"2011-01-02"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"text":{
|
|
||||||
"status":"generated",
|
|
||||||
"div":"<div xmlns=\"http://www.w3.org/1999/xhtml\">\n <table>\n <tbody>\n <tr>\n <td>Name</td>\n <td>Peter James <b>Chalmers</b> (\"Jim\")</td>\n </tr>\n <tr>\n <td>Address</td>\n <td>534 Erewhon, Pleasantville, Vic, 3999</td>\n </tr>\n <tr>\n <td>Contacts</td>\n <td>Home: unknown. Work: (03) 5555 6473</td>\n </tr>\n <tr>\n <td>Id</td>\n <td>MRN: 12345 (Acme Healthcare)</td>\n </tr>\n </tbody>\n </table>\n </div>"
|
|
||||||
},
|
|
||||||
"identifier":[
|
|
||||||
{
|
|
||||||
"use":"usual",
|
|
||||||
"label":"MRN",
|
|
||||||
"system":"urn:oid:1.2.36.146.595.217.0.1",
|
|
||||||
"value":"12345",
|
|
||||||
"period":{
|
|
||||||
"start":"2001-05-06"
|
|
||||||
},
|
|
||||||
"assigner":{
|
|
||||||
"display":"Acme Healthcare"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name":[
|
|
||||||
{
|
|
||||||
"use":"official",
|
|
||||||
"family":[
|
|
||||||
"Chalmers"
|
|
||||||
],
|
|
||||||
"given":[
|
|
||||||
"Peter",
|
|
||||||
"James"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"use":"usual",
|
|
||||||
"given":[
|
|
||||||
"Jim"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"telecom":[
|
|
||||||
{
|
|
||||||
"use":"home"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"system":"phone",
|
|
||||||
"value":"(03) 5555 6473",
|
|
||||||
"use":"work"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gender":{
|
|
||||||
"coding":[
|
|
||||||
{
|
|
||||||
"system":"http://hl7.org/fhir/v3/AdministrativeGender",
|
|
||||||
"code":"M",
|
|
||||||
"display":"Male"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"birthDate":"1974-12-25",
|
|
||||||
"deceasedBoolean":false,
|
|
||||||
"address":[
|
|
||||||
{
|
|
||||||
"use":"home",
|
|
||||||
"line":[
|
|
||||||
"534 Erewhon St"
|
|
||||||
],
|
|
||||||
"city":"PleasantVille",
|
|
||||||
"state":"Vic",
|
|
||||||
"zip":"3999"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"use":"old",
|
|
||||||
"line":[
|
|
||||||
"SecondAddress"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contact":[
|
|
||||||
{
|
|
||||||
"relationship":[
|
|
||||||
{
|
|
||||||
"coding":[
|
|
||||||
{
|
|
||||||
"system":"http://hl7.org/fhir/patient-contact-relationship",
|
|
||||||
"code":"partner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name":{
|
|
||||||
"family":[
|
|
||||||
"du",
|
|
||||||
"Marché"
|
|
||||||
],
|
|
||||||
"_family":[
|
|
||||||
{
|
|
||||||
"extension":[
|
|
||||||
{
|
|
||||||
"url":"http://hl7.org/fhir/Profile/iso-21090#qualifier",
|
|
||||||
"valuecode":"VV"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"given":[
|
|
||||||
"Bénédicte"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"telecom":[
|
|
||||||
{
|
|
||||||
"system":"phone",
|
|
||||||
"value":"+33 (237) 998327"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"managingOrganization":{
|
|
||||||
"resource":"Organization/1"
|
|
||||||
},
|
},
|
||||||
"active":true
|
"active":true
|
||||||
}
|
}
|
|
@ -1,2 +1,4 @@
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/main/java=UTF-8
|
||||||
|
encoding//src/test/java=UTF-8
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|
|
@ -65,6 +65,7 @@ import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
||||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||||
|
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -967,11 +968,14 @@ public class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> values;
|
List<String> values;
|
||||||
|
boolean addToWhere=true;
|
||||||
if ("token".equals(nextType)) {
|
if ("token".equals(nextType)) {
|
||||||
if (isBlank(parts.get(2))) {
|
if (isBlank(parts.get(2))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
values = Collections.singletonList(StringUtils.join(parts, ""));
|
values = Collections.singletonList(StringUtils.join(parts, ""));
|
||||||
|
addToWhere=false;
|
||||||
|
theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().systemAndCode(parts.get(0), parts.get(2)));
|
||||||
} else if ("date".equals(nextType)) {
|
} else if ("date".equals(nextType)) {
|
||||||
values = new ArrayList<String>();
|
values = new ArrayList<String>();
|
||||||
if (isNotBlank(parts.get(1))) {
|
if (isNotBlank(parts.get(1))) {
|
||||||
|
@ -998,8 +1002,9 @@ public class Controller {
|
||||||
theClientCodeJsonWriter.write("qualifier", nextQualifier);
|
theClientCodeJsonWriter.write("qualifier", nextQualifier);
|
||||||
theClientCodeJsonWriter.write("value", nextValue);
|
theClientCodeJsonWriter.write("value", nextValue);
|
||||||
theClientCodeJsonWriter.writeEnd();
|
theClientCodeJsonWriter.writeEnd();
|
||||||
|
if (addToWhere) {
|
||||||
theQuery.where(new StringClientParam(nextName + nextQualifier).matches().value(nextValue));
|
theQuery.where(new StringClientParam(nextName + nextQualifier).matches().value(nextValue));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=UTF-8
|
encoding//src/main/java=UTF-8
|
||||||
encoding//src/main/resources=UTF-8
|
encoding//src/main/resources=UTF-8
|
||||||
|
encoding//src/test/java=UTF-8
|
||||||
encoding//src/test/resources=UTF-8
|
encoding//src/test/resources=UTF-8
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|
Loading…
Reference in New Issue