Lots of tester enhancements
This commit is contained in:
parent
f87c50b4b4
commit
14befcd091
|
@ -22,14 +22,15 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<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.7">
|
||||
<attributes>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
|
@ -15,9 +20,17 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<wb-module deploy-name="hapi-fhir-base">
|
||||
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
||||
</wb-module>
|
||||
</project-modules>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<installed facet="jst.utility" version="1.0"/>
|
||||
<installed facet="java" version="1.6"/>
|
||||
</faceted-project>
|
|
@ -55,7 +55,7 @@
|
|||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
<version>${thymeleaf-version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
@ -101,6 +101,13 @@
|
|||
<version>4.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${spring_version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Server -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
|
@ -381,7 +388,9 @@
|
|||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-1395874-5', 'sourceforge.net');
|
||||
ga('create', 'UA-1395874-5', 'auto');
|
||||
ga('require', 'displayfeatures');
|
||||
ga('require', 'linkid', 'linkid.js');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -60,6 +60,9 @@
|
|||
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
|
||||
produced by the Health Intersections server)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server now automatically compresses responses if the client indicates support
|
||||
</action>
|
||||
</release>
|
||||
</body>
|
||||
</document>
|
||||
|
|
|
@ -93,10 +93,6 @@ public class FhirContext {
|
|||
return myClassToElementDefinition.get(theElementType);
|
||||
}
|
||||
|
||||
public FhirTerser newTerser() {
|
||||
return new FhirTerser(this);
|
||||
}
|
||||
|
||||
public INarrativeGenerator getNarrativeGenerator() {
|
||||
return myNarrativeGenerator;
|
||||
}
|
||||
|
@ -226,6 +222,10 @@ public class FhirContext {
|
|||
return getRestfulClientFactory().newGenericClient(theServerBase);
|
||||
}
|
||||
|
||||
public FhirTerser newTerser() {
|
||||
return new FhirTerser(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new JSON parser.
|
||||
*
|
||||
|
@ -241,6 +241,10 @@ public class FhirContext {
|
|||
myNarrativeGenerator = theNarrativeGenerator;
|
||||
}
|
||||
|
||||
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
|
||||
myRestfulClientFactory = theRestfulClientFactory;
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition scanResourceType(Class<? extends IResource> theResourceType) {
|
||||
ArrayList<Class<? extends IResource>> resourceTypes = new ArrayList<Class<? extends IResource>>();
|
||||
resourceTypes.add(theResourceType);
|
||||
|
@ -249,7 +253,7 @@ public class FhirContext {
|
|||
}
|
||||
|
||||
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IResource>> theResourceTypes) {
|
||||
ModelScanner scanner = new ModelScanner(theResourceTypes);
|
||||
ModelScanner scanner = new ModelScanner(myClassToElementDefinition, theResourceTypes);
|
||||
if (myRuntimeChildUndeclaredExtensionDefinition == null) {
|
||||
myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
|
||||
}
|
||||
|
@ -273,6 +277,11 @@ public class FhirContext {
|
|||
return classToElementDefinition;
|
||||
}
|
||||
|
||||
/** For unit tests only */
|
||||
int getElementDefinitionCount() {
|
||||
return myClassToElementDefinition.size();
|
||||
}
|
||||
|
||||
private static Collection<Class<? extends IResource>> toCollection(Class<? extends IResource> theResourceType) {
|
||||
ArrayList<Class<? extends IResource>> retVal = new ArrayList<Class<? extends IResource>>(1);
|
||||
retVal.add(theResourceType);
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Iterator;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
@ -90,11 +91,15 @@ class ModelScanner {
|
|||
ModelScanner(Class<? extends IResource> theResourceTypes) throws ConfigurationException {
|
||||
Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>();
|
||||
singleton.add(theResourceTypes);
|
||||
init(singleton);
|
||||
init(null,singleton);
|
||||
}
|
||||
|
||||
ModelScanner(Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
|
||||
init(new HashSet<Class<? extends IElement>>(theResourceTypes));
|
||||
init(null, new HashSet<Class<? extends IElement>>(theResourceTypes));
|
||||
}
|
||||
|
||||
ModelScanner(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
|
||||
init(theExistingDefinitions, new HashSet<Class<? extends IElement>>(theResourceTypes));
|
||||
}
|
||||
|
||||
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
|
||||
|
@ -147,7 +152,12 @@ class ModelScanner {
|
|||
}
|
||||
}
|
||||
|
||||
private void init(Set<Class<? extends IElement>> toScan) {
|
||||
private void init(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Set<Class<? extends IElement>> toScan) {
|
||||
if (theExistingDefinitions!=null) {
|
||||
myClassToElementDefinitions.putAll(theExistingDefinitions);
|
||||
}
|
||||
|
||||
int startSize = myClassToElementDefinitions.size();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/model.properties");
|
||||
|
@ -199,7 +209,11 @@ class ModelScanner {
|
|||
myScanAlso.clear();
|
||||
} while (!toScan.isEmpty());
|
||||
|
||||
for (BaseRuntimeElementDefinition<?> next : myClassToElementDefinitions.values()) {
|
||||
for (Entry<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> nextEntry : myClassToElementDefinitions.entrySet()) {
|
||||
if (theExistingDefinitions!=null&&theExistingDefinitions.containsKey(nextEntry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> next = nextEntry.getValue();
|
||||
next.sealAndInitialize(myClassToElementDefinitions);
|
||||
}
|
||||
|
||||
|
@ -207,7 +221,8 @@ class ModelScanner {
|
|||
myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions);
|
||||
|
||||
long time = System.currentTimeMillis() - start;
|
||||
ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", myClassToElementDefinitions.size(), time);
|
||||
int size = myClassToElementDefinitions.size()- startSize;
|
||||
ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", size, time);
|
||||
}
|
||||
|
||||
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
|
||||
|
|
|
@ -31,7 +31,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
|
||||
public abstract class ResourceMetadataKeyEnum<T> {
|
||||
|
||||
|
||||
/**
|
||||
* If present and populated with a date/time (as an instance of {@link InstantDt}),
|
||||
* this value is an indication that the resource is in the deleted state. This key
|
||||
|
@ -100,7 +99,6 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The value for this key is the list of tags associated with this resource
|
||||
* <p>
|
||||
|
@ -155,6 +153,7 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The value for this key is the version ID of the resource object.
|
||||
* <p>
|
||||
|
@ -178,19 +177,11 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
|
||||
private final String myValue;
|
||||
|
||||
|
||||
|
||||
public ResourceMetadataKeyEnum(String theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
@ -209,6 +200,27 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
return true;
|
||||
}
|
||||
|
||||
public abstract T get(IResource theResource);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract void put(IResource theResource, T theObject);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
private String name() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
||||
Object retValObj = theResourceMetadata.get(theKey);
|
||||
if (retValObj == null) {
|
||||
|
@ -231,10 +243,6 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
|
||||
}
|
||||
|
||||
private String name() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
|
||||
Object retValObj = theResourceMetadata.get(theKey);
|
||||
if (retValObj == null) {
|
||||
|
@ -251,8 +259,4 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
|||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
|
||||
}
|
||||
|
||||
public abstract T get(IResource theResource);
|
||||
|
||||
public abstract void put(IResource theResource, T theObject);
|
||||
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
|
||||
String body=null;
|
||||
Reader reader=null;
|
||||
|
|
|
@ -28,7 +28,8 @@ import java.util.Map;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -38,9 +39,12 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
|||
|
||||
public class RestfulClientFactory implements IRestfulClientFactory {
|
||||
|
||||
private int myConnectionRequestTimeout=10000;
|
||||
private int myConnectTimeout=10000;
|
||||
private FhirContext myContext;
|
||||
private HttpClient myHttpClient;
|
||||
private Map<Class<? extends IRestfulClient>, ClientInvocationHandler> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandler>();
|
||||
private int mySocketTimeout = 10000;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -48,14 +52,63 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
* @param theContext
|
||||
* The context
|
||||
*/
|
||||
public RestfulClientFactory(FhirContext theContext) {
|
||||
public RestfulClientFactory(FhirContext theFhirContext) {
|
||||
myContext=theFhirContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public RestfulClientFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context associated with this client factory. Must not be called
|
||||
* more than once.
|
||||
*/
|
||||
public void setFhirContext(FhirContext theContext) {
|
||||
if(myContext!=null&&myContext!=theContext) {
|
||||
throw new IllegalStateException("RestfulClientFactory instance is already associated with one FhirContext. RestfulClientFactory instances can not be shared.");
|
||||
}
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends IRestfulClient> T instantiateProxy(Class<T> theClientType, InvocationHandler theInvocationHandler) {
|
||||
T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
|
||||
return proxy;
|
||||
public int getConnectionRequestTimeout() {
|
||||
return myConnectionRequestTimeout;
|
||||
}
|
||||
|
||||
public int getConnectTimeout() {
|
||||
return myConnectTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized HttpClient getHttpClient() {
|
||||
if (myHttpClient == null) {
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
|
||||
//@formatter:off
|
||||
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
||||
.setSocketTimeout(mySocketTimeout)
|
||||
.setConnectTimeout(myConnectTimeout)
|
||||
.setConnectionRequestTimeout(myConnectionRequestTimeout)
|
||||
.setStaleConnectionCheckEnabled(true)
|
||||
.build();
|
||||
|
||||
myHttpClient = HttpClients.custom()
|
||||
.setConnectionManager(connectionManager)
|
||||
.setDefaultRequestConfig(defaultRequestConfig)
|
||||
.disableCookieManagement()
|
||||
.build();
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
return myHttpClient;
|
||||
}
|
||||
|
||||
public int getSocketTimeout() {
|
||||
return mySocketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +139,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
if (invocationHandler == null) {
|
||||
invocationHandler = new ClientInvocationHandler(client, myContext, serverBase, theClientType);
|
||||
for (Method nextMethod : theClientType.getMethods()) {
|
||||
BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null);
|
||||
BaseMethodBinding<?> binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null);
|
||||
invocationHandler.addBinding(nextMethod, binding);
|
||||
}
|
||||
myInvocationHandlers.put(theClientType, invocationHandler);
|
||||
|
@ -98,14 +151,18 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public synchronized HttpClient getHttpClient() {
|
||||
if (myHttpClient == null) {
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
myHttpClient = builder.build();
|
||||
public IGenericClient newGenericClient(String theServerBase) {
|
||||
return new GenericClient(myContext, getHttpClient(), theServerBase);
|
||||
}
|
||||
return myHttpClient;
|
||||
|
||||
public synchronized void setConnectionRequestTimeout(int theConnectionRequestTimeout) {
|
||||
myConnectionRequestTimeout = theConnectionRequestTimeout;
|
||||
myHttpClient=null;
|
||||
}
|
||||
|
||||
public synchronized void setConnectTimeout(int theConnectTimeout) {
|
||||
myConnectTimeout = theConnectTimeout;
|
||||
myHttpClient=null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,9 +177,15 @@ public class RestfulClientFactory implements IRestfulClientFactory {
|
|||
myHttpClient = theHttpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGenericClient newGenericClient(String theServerBase) {
|
||||
return new GenericClient(myContext, getHttpClient(), theServerBase);
|
||||
public synchronized void setSocketTimeout(int theSocketTimeout) {
|
||||
mySocketTimeout = theSocketTimeout;
|
||||
myHttpClient=null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends IRestfulClient> T instantiateProxy(Class<T> theClientType, InvocationHandler theInvocationHandler) {
|
||||
T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -260,12 +260,15 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
b.append('/');
|
||||
b.append(getResourceName());
|
||||
b.append('/');
|
||||
b.append(response.getId().getValue());
|
||||
if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||
b.append("/_history/");
|
||||
b.append(response.getId().getIdPart());
|
||||
if (response.getId().hasVersionIdPart()) {
|
||||
b.append("/"+Constants.PARAM_HISTORY+"/");
|
||||
b.append(response.getId().getVersionIdPart());
|
||||
}else if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||
b.append("/"+Constants.PARAM_HISTORY+"/");
|
||||
b.append(response.getVersionId().getValue());
|
||||
}
|
||||
theResponse.addHeader("Location", b.toString());
|
||||
theResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
|
||||
}
|
||||
|
||||
private static void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) {
|
||||
|
@ -396,7 +399,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
}
|
||||
|
||||
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
|
||||
List<String> locationHeaders = theHeaders.get("location");
|
||||
List<String> locationHeaders = theHeaders.get(Constants.HEADER_CONTENT_LOCATION_LC);
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
if (locationHeaders != null && locationHeaders.size() > 0) {
|
||||
String locationHeader = locationHeaders.get(0);
|
||||
|
|
|
@ -218,10 +218,12 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
Integer count = RestfulServer.extractCountParameter(theRequest.getServletRequest());
|
||||
|
||||
boolean respondGzip=theRequest.isRespondGzip();
|
||||
|
||||
IBundleProvider result = invokeServer(theRequest, params);
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE:
|
||||
RestfulServer.streamResponseAsBundle(theServer, theResponse, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, 0, count, null);
|
||||
RestfulServer.streamResponseAsBundle(theServer, theResponse, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, 0, count, null, respondGzip);
|
||||
break;
|
||||
case RESOURCE:
|
||||
if (result.size() == 0) {
|
||||
|
@ -229,7 +231,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
} else if (result.size() > 1) {
|
||||
throw new InternalErrorException("Method returned multiple resources");
|
||||
}
|
||||
RestfulServer.streamResponseAsResource(theServer, theResponse, result.getResources(0, 1).get(0), responseEncoding, prettyPrint, requestIsBrowser, narrativeMode);
|
||||
RestfulServer.streamResponseAsResource(theServer, theResponse, result.getResources(0, 1).get(0), responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package ca.uhn.fhir.rest.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -48,6 +47,7 @@ public class Request {
|
|||
private HttpServletResponse myServletResponse;
|
||||
private IdDt myVersion;
|
||||
private Map<String,List<String>> myUnqualifiedToQualifiedNames;
|
||||
private boolean myRespondGzip;
|
||||
|
||||
public String getCompleteUrl() {
|
||||
return myCompleteUrl;
|
||||
|
@ -177,4 +177,12 @@ public class Request {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public void setRespondGzip(boolean theRespondGzip) {
|
||||
myRespondGzip=theRespondGzip;
|
||||
}
|
||||
|
||||
public boolean isRespondGzip() {
|
||||
return myRespondGzip;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -73,9 +73,8 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
@Override
|
||||
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
|
||||
/*
|
||||
* We are being a bit lenient here, since technically the client is
|
||||
* supposed to include the version in the Content-Location header, but
|
||||
* we allow it in the PUT URL as well..
|
||||
* We are being a bit lenient here, since technically the client is supposed to include the version in the
|
||||
* Content-Location header, but we allow it in the PUT URL as well..
|
||||
*/
|
||||
String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
IdDt id = new IdDt(locationHeader);
|
||||
|
@ -126,15 +125,24 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
}
|
||||
|
||||
public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) {
|
||||
String id = idDt.getValue();
|
||||
String resourceName = context.getResourceDefinition(theResource).getName();
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(resourceName);
|
||||
urlExtension.append('/');
|
||||
urlExtension.append(id);
|
||||
urlExtension.append(idDt.getIdPart());
|
||||
HttpPutClientInvocation retVal = new HttpPutClientInvocation(context, theResource, urlExtension.toString());
|
||||
|
||||
if (versionIdDt != null) {
|
||||
if (idDt.hasVersionIdPart()) {
|
||||
String versionId = versionIdDt.getValue();
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('/');
|
||||
b.append(urlExtension);
|
||||
b.append("/_history/");
|
||||
b.append(versionId);
|
||||
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
|
||||
}
|
||||
} else if (versionIdDt != null) {
|
||||
String versionId = versionIdDt.getValue();
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
|
|
@ -45,6 +45,7 @@ public class Constants {
|
|||
public static final String HEADER_ACCEPT = "Accept";
|
||||
public static final String HEADER_CATEGORY = "Category";
|
||||
public static final String HEADER_CONTENT_LOCATION = "Content-Location";
|
||||
public static final String HEADER_CONTENT_LOCATION_LC = HEADER_CONTENT_LOCATION.toLowerCase();
|
||||
public static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||
public static final String HEADER_LAST_MODIFIED = "Last-Modified";
|
||||
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
|
||||
|
@ -81,6 +82,9 @@ public class Constants {
|
|||
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
|
||||
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
|
||||
public static final String URL_TOKEN_HISTORY = "_history";
|
||||
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
|
||||
public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||
public static final String ENCODING_GZIP = "gzip";
|
||||
|
||||
|
||||
static {
|
||||
|
|
|
@ -23,8 +23,10 @@ package ca.uhn.fhir.rest.server;
|
|||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URLEncoder;
|
||||
|
@ -36,6 +38,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
@ -352,7 +355,8 @@ public class RestfulServer extends HttpServlet {
|
|||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction);
|
||||
boolean respondGzip=theRequest.isRespondGzip();
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, respondGzip);
|
||||
|
||||
}
|
||||
|
||||
|
@ -488,6 +492,17 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
// TODO: look for more tokens for version, compartments, etc...
|
||||
|
||||
String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING);
|
||||
boolean respondGzip=false;
|
||||
if (acceptEncoding != null) {
|
||||
String[] parts = acceptEncoding.trim().split("\\s*,\\s*");
|
||||
for (String string : parts) {
|
||||
if (string.equals("gzip")) {
|
||||
respondGzip=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Request r = new Request();
|
||||
r.setResourceName(resourceName);
|
||||
r.setId(id);
|
||||
|
@ -500,6 +515,7 @@ public class RestfulServer extends HttpServlet {
|
|||
r.setCompleteUrl(completeUrl);
|
||||
r.setServletRequest(theRequest);
|
||||
r.setServletResponse(theResponse);
|
||||
r.setRespondGzip(respondGzip);
|
||||
|
||||
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||
|
@ -526,10 +542,14 @@ public class RestfulServer extends HttpServlet {
|
|||
theResponse.setContentType("text/plain");
|
||||
theResponse.setCharacterEncoding("UTF-8");
|
||||
theResponse.getWriter().write(e.getMessage());
|
||||
} catch (BaseServerResponseException e) {
|
||||
} catch (Throwable e) {
|
||||
|
||||
int statusCode = 500;
|
||||
if (e instanceof InternalErrorException) {
|
||||
ourLog.error("Failure during REST processing", e);
|
||||
} else if (e instanceof BaseServerResponseException) {
|
||||
ourLog.warn("Failure during REST processing: {}", e.toString());
|
||||
statusCode=((BaseServerResponseException) e).getStatusCode();
|
||||
} else {
|
||||
ourLog.warn("Failure during REST processing: {}", e.toString());
|
||||
}
|
||||
|
@ -539,19 +559,15 @@ public class RestfulServer extends HttpServlet {
|
|||
issue.getSeverity().setValueAsEnum(IssueSeverityEnum.ERROR);
|
||||
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
||||
|
||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, e.getStatusCode());
|
||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode,false);
|
||||
|
||||
theResponse.setStatus(e.getStatusCode());
|
||||
theResponse.setStatus(statusCode);
|
||||
addHeadersToResponse(theResponse);
|
||||
theResponse.setContentType("text/plain");
|
||||
theResponse.setCharacterEncoding("UTF-8");
|
||||
theResponse.getWriter().append(e.getMessage());
|
||||
theResponse.getWriter().close();
|
||||
|
||||
} catch (Throwable t) {
|
||||
// TODO: handle this better
|
||||
ourLog.error("Failed to process invocation", t);
|
||||
throw new ServletException(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -666,7 +682,6 @@ public class RestfulServer extends HttpServlet {
|
|||
myPlainProviders = theProviders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on this server.
|
||||
*
|
||||
|
@ -685,7 +700,6 @@ public class RestfulServer extends HttpServlet {
|
|||
myPlainProviders = Arrays.asList(theProviders);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
|
@ -769,7 +783,7 @@ public class RestfulServer extends HttpServlet {
|
|||
return bundle;
|
||||
}
|
||||
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount) {
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
b.append('?');
|
||||
|
@ -788,6 +802,16 @@ public class RestfulServer extends HttpServlet {
|
|||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(theCount);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_FORMAT);
|
||||
b.append('=');
|
||||
b.append(theResponseEncoding.getRequestContentType());
|
||||
if (thePrettyPrint) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PRETTY);
|
||||
b.append('=');
|
||||
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
|
@ -911,7 +935,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId) throws IOException {
|
||||
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
@ -965,17 +989,23 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
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(createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn));
|
||||
bundle.getLinkNext().setValue(createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - numToReturn);
|
||||
bundle.getLinkPrevious().setValue(createPagingLink(theServerBase, searchId, start, numToReturn));
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
bundle.getLinkPrevious().setValue(createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter writer = theHttpResponse.getWriter();
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
for (IResource next : resourceList) {
|
||||
|
@ -990,13 +1020,24 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
int stausCode = 200;
|
||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode);
|
||||
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) throws IOException {
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException {
|
||||
int stausCode = 200;
|
||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip);
|
||||
}
|
||||
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip)
|
||||
throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
if (theResource instanceof Binary) {
|
||||
|
@ -1041,7 +1082,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
PrintWriter writer = theHttpResponse.getWriter();
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
writer.append(theResource.getText().getDiv().getValueAsString());
|
||||
|
|
|
@ -46,6 +46,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
|||
registerExceptionType(ResourceVersionNotSpecifiedException.STATUS_CODE, ResourceVersionNotSpecifiedException.class);
|
||||
registerExceptionType(ResourceVersionConflictException.STATUS_CODE, ResourceVersionConflictException.class);
|
||||
registerExceptionType(UnprocessableEntityException.STATUS_CODE, UnprocessableEntityException.class);
|
||||
registerExceptionType(ResourceGoneException.STATUS_CODE, ResourceGoneException.class);
|
||||
}
|
||||
|
||||
private final OperationOutcome myOperationOutcome;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package ca.uhn.fhir.rest.server.exceptions;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR 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%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
/**
|
||||
* Represents an <b>HTTP 410 Resource Gone</b> response, which gvenerally
|
||||
* indicates that the resource has been deleted
|
||||
*/
|
||||
public class ResourceGoneException extends BaseServerResponseException {
|
||||
|
||||
public static final int STATUS_CODE = Constants.STATUS_HTTP_410_GONE;
|
||||
|
||||
public ResourceGoneException(IdDt theId) {
|
||||
super(STATUS_CODE, "Resource " + (theId != null ? theId.getValue() : "") + " is gone/deleted");
|
||||
}
|
||||
|
||||
public ResourceGoneException(Class<? extends IResource> theClass, IdentifierDt thePatientId) {
|
||||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted");
|
||||
}
|
||||
|
||||
public ResourceGoneException(Class<? extends IResource> theClass, IdDt thePatientId) {
|
||||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is gone/deleted");
|
||||
}
|
||||
|
||||
public ResourceGoneException(String theMessage) {
|
||||
super(STATUS_CODE, theMessage);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
|
@ -1,508 +0,0 @@
|
|||
package ca.uhn.fhir.rest.server.tester;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR 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%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.WriterOutputStream;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.TemplateProcessingParameters;
|
||||
import org.thymeleaf.context.WebContext;
|
||||
import org.thymeleaf.resourceresolver.IResourceResolver;
|
||||
import org.thymeleaf.standard.StandardDialect;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class RestfulServerTesterServlet extends HttpServlet {
|
||||
|
||||
private static final boolean DEBUGMODE = true;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerTesterServlet.class);
|
||||
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
|
||||
private static final long serialVersionUID = 1L;
|
||||
private FhirContext myCtx;
|
||||
private String myServerBase;
|
||||
private HashMap<String, String> myStaticResources;
|
||||
|
||||
private TemplateEngine myTemplateEngine;
|
||||
private Set<String> myFilterHeaders;
|
||||
|
||||
public RestfulServerTesterServlet() {
|
||||
myStaticResources = new HashMap<String, String>();
|
||||
myStaticResources.put("jquery-2.1.0.min.js", "text/javascript");
|
||||
myStaticResources.put("PublicTester.js", "text/javascript");
|
||||
myStaticResources.put("PublicTester.css", "text/css");
|
||||
myStaticResources.put("hapi_fhir_banner.png", "image/png");
|
||||
myStaticResources.put("hapi_fhir_banner_right.png", "image/png");
|
||||
myStaticResources.put("shCore.js", "text/javascript");
|
||||
myStaticResources.put("shBrushJScript.js", "text/javascript");
|
||||
myStaticResources.put("shBrushXml.js", "text/javascript");
|
||||
myStaticResources.put("shBrushPlain.js", "text/javascript");
|
||||
myStaticResources.put("shCore.css", "text/css");
|
||||
myStaticResources.put("shThemeDefault.css", "text/css");
|
||||
myStaticResources.put("json2.js", "text/javascript");
|
||||
myStaticResources.put("minify.json.js", "text/javascript");
|
||||
|
||||
myCtx = new FhirContext();
|
||||
}
|
||||
|
||||
public FhirContext getFhirContext() {
|
||||
return myCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig theConfig) throws ServletException {
|
||||
myTemplateEngine = new TemplateEngine();
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new ProfileResourceResolver());
|
||||
myTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
myTemplateEngine.setDialect(dialect);
|
||||
myTemplateEngine.initialize();
|
||||
}
|
||||
|
||||
public void setServerBase(String theServerBase) {
|
||||
myServerBase = theServerBase;
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
|
||||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException {
|
||||
InputStream res = RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName);
|
||||
theResp.setContentType(theContentType);
|
||||
IOUtils.copy(res, theResp.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
try {
|
||||
ourLog.info("RequestURI: {}", theReq.getPathInfo());
|
||||
|
||||
String resName = theReq.getPathInfo().substring(1);
|
||||
if (myStaticResources.containsKey(resName)) {
|
||||
streamResponse(resName, myStaticResources.get(resName), theResp);
|
||||
return;
|
||||
}
|
||||
|
||||
ConformanceClient client = myCtx.newRestfulClient(ConformanceClient.class, myServerBase);
|
||||
Conformance conformance = client.getConformance();
|
||||
|
||||
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
|
||||
ctx.setVariable("conf", conformance);
|
||||
ctx.setVariable("base", myServerBase);
|
||||
ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance));
|
||||
addStandardVariables(ctx, theReq.getParameterMap());
|
||||
|
||||
theResp.setContentType("text/html");
|
||||
theResp.setCharacterEncoding("UTF-8");
|
||||
|
||||
myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to respond", e);
|
||||
theResp.sendError(500, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void addStandardVariables(WebContext theCtx, Map<String, String[]> theParameterMap) {
|
||||
addStandardVariable(theCtx, theParameterMap, "configEncoding");
|
||||
addStandardVariable(theCtx, theParameterMap, "configPretty");
|
||||
}
|
||||
|
||||
private void addStandardVariable(WebContext theCtx, Map<String, String[]> theParameterMap, String key) {
|
||||
if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) {
|
||||
theCtx.setVariable(key, theParameterMap.get(key)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
|
||||
client.setKeepResponses(true);
|
||||
boolean returnsResource;
|
||||
long latency=0;
|
||||
|
||||
try {
|
||||
String method = theReq.getParameter("method");
|
||||
|
||||
String prettyParam = theReq.getParameter("configPretty");
|
||||
if ("on".equals(prettyParam)) {
|
||||
client.setPrettyPrint(true);
|
||||
}
|
||||
if ("xml".equals(theReq.getParameter("configEncoding"))) {
|
||||
client.setEncoding(EncodingEnum.XML);
|
||||
} else if ("json".equals(theReq.getParameter("configEncoding"))) {
|
||||
client.setEncoding(EncodingEnum.JSON);
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
if ("conformance".equals(method)) {
|
||||
returnsResource = true;
|
||||
client.conformance();
|
||||
} else if ("read".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
returnsResource = true;
|
||||
|
||||
client.read(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else if ("vread".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
|
||||
String versionId = StringUtils.defaultString(theReq.getParameter("versionid"));
|
||||
if (StringUtils.isBlank(versionId)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified");
|
||||
}
|
||||
returnsResource = true;
|
||||
|
||||
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
|
||||
|
||||
} else if ("delete".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
|
||||
returnsResource = false;
|
||||
|
||||
client.delete(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else if ("history-instance".equals(method) || "history-server".equals(method) || "history-type".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
|
||||
returnsResource = false;
|
||||
|
||||
client.history(def.getImplementingClass(), new IdDt(id),null,null);
|
||||
|
||||
} else if ("create".equals(method)) {
|
||||
IResource resource = parseIncomingResource(theReq, theResp, client);
|
||||
returnsResource = false;
|
||||
|
||||
client.create(resource);
|
||||
|
||||
} else if ("validate".equals(method)) {
|
||||
IResource resource = parseIncomingResource(theReq, theResp, client);
|
||||
returnsResource = false;
|
||||
|
||||
client.validate(resource);
|
||||
|
||||
} else if ("update".equals(method)) {
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
|
||||
IResource resource = parseIncomingResource(theReq, theResp, client);
|
||||
returnsResource = false;
|
||||
|
||||
client.update(new IdDt(id), resource);
|
||||
|
||||
} else if ("searchType".equals(method)) {
|
||||
Map<String, List<IQueryParameterType>> params = new HashMap<String, List<IQueryParameterType>>();
|
||||
|
||||
HashSet<String> hashSet = new HashSet<String>(theReq.getParameterMap().keySet());
|
||||
String paramName = null;
|
||||
IQueryParameterType paramValue = null;
|
||||
while (hashSet.isEmpty() == false) {
|
||||
|
||||
String nextKey = hashSet.iterator().next();
|
||||
String nextValue = theReq.getParameter(nextKey);
|
||||
paramName = null;
|
||||
paramValue = null;
|
||||
|
||||
if (nextKey.startsWith("param.token.")) {
|
||||
int prefixLength = "param.token.".length();
|
||||
paramName = nextKey.substring(prefixLength + 2);
|
||||
String systemKey = "param.token." + "1." + paramName;
|
||||
String valueKey = "param.token." + "2." + paramName;
|
||||
String system = theReq.getParameter(systemKey);
|
||||
String value = theReq.getParameter(valueKey);
|
||||
paramValue = new IdentifierDt(system, value);
|
||||
hashSet.remove(systemKey);
|
||||
hashSet.remove(valueKey);
|
||||
} else if (nextKey.startsWith("param.string.")) {
|
||||
paramName = nextKey.substring("param.string.".length());
|
||||
paramValue = new StringDt(nextValue);
|
||||
}
|
||||
|
||||
if (paramName != null) {
|
||||
if (params.containsKey(paramName) == false) {
|
||||
params.put(paramName, new ArrayList<IQueryParameterType>());
|
||||
}
|
||||
params.get(paramName).add(paramValue);
|
||||
}
|
||||
|
||||
hashSet.remove(nextKey);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
|
||||
returnsResource = false;
|
||||
client.search(def.getImplementingClass(), params);
|
||||
|
||||
} else {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
|
||||
return;
|
||||
}
|
||||
|
||||
latency = System.currentTimeMillis() - start;
|
||||
} catch (DataFormatException e) {
|
||||
ourLog.error("Failed to invoke method", e);
|
||||
returnsResource = false;
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
returnsResource = false;
|
||||
}
|
||||
|
||||
try {
|
||||
HttpRequestBase lastRequest = client.getLastRequest();
|
||||
String requestBody = null;
|
||||
String requestSyntaxHighlighterClass = null;
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntityEnclosingRequest lastEERequest = (HttpEntityEnclosingRequest) lastRequest;
|
||||
HttpEntity lastEE = lastEERequest.getEntity();
|
||||
if (lastEE.isRepeatable()) {
|
||||
StringWriter requestCapture = new StringWriter();
|
||||
lastEE.writeTo(new WriterOutputStream(requestCapture, "UTF-8"));
|
||||
requestBody = requestCapture.toString();
|
||||
ContentType ct = ContentType.get(lastEE);
|
||||
String mimeType = ct.getMimeType();
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
if (ctEnum == null) {
|
||||
requestSyntaxHighlighterClass = "brush: plain";
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
requestSyntaxHighlighterClass = "brush: jscript";
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
requestSyntaxHighlighterClass = "brush: xml";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String resultSyntaxHighlighterClass;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null;
|
||||
String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null;
|
||||
String resultBody = client.getLastResponseBody();
|
||||
|
||||
HttpResponse lastResponse = client.getLastResponse();
|
||||
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
String narrativeString = "";
|
||||
|
||||
if (ctEnum == null) {
|
||||
resultSyntaxHighlighterClass = "brush: plain";
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
resultSyntaxHighlighterClass = "brush: jscript";
|
||||
if (returnsResource) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
resultSyntaxHighlighterClass = "brush: xml";
|
||||
if (returnsResource) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
|
||||
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
|
||||
|
||||
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
|
||||
ctx.setVariable("base", myServerBase);
|
||||
ctx.setVariable("requestUrl", requestUrl);
|
||||
ctx.setVariable("action", action);
|
||||
ctx.setVariable("resultStatus", resultStatus);
|
||||
ctx.setVariable("requestBody", StringEscapeUtils.escapeHtml4(requestBody));
|
||||
ctx.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass);
|
||||
ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody));
|
||||
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
|
||||
ctx.setVariable("requestHeaders", requestHeaders);
|
||||
ctx.setVariable("responseHeaders", responseHeaders);
|
||||
ctx.setVariable("narrative", narrativeString);
|
||||
ctx.setVariable("latencyMs", latency);
|
||||
|
||||
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theResp.sendError(500, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private IResource parseIncomingResource(HttpServletRequest theReq, HttpServletResponse theResp, GenericClient theClient) throws ServletException, IOException {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
|
||||
if (StringUtils.isBlank(resourceText)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
|
||||
}
|
||||
|
||||
IResource resource;
|
||||
if (theClient.getEncoding() == null) {
|
||||
if (resourceText.trim().startsWith("{")) {
|
||||
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
} else {
|
||||
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
}
|
||||
} else if (theClient.getEncoding() == EncodingEnum.XML) {
|
||||
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
} else {
|
||||
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user.
|
||||
* This can be used, for instance, to filter out "Authorization" headers. Note that names are not case sensitive.
|
||||
*/
|
||||
public void setFilterHeaders(String... theHeaderNames) {
|
||||
myFilterHeaders = new HashSet<String>();
|
||||
if (theHeaderNames != null) {
|
||||
for (String next : theHeaderNames) {
|
||||
myFilterHeaders.add(next.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private interface ConformanceClient extends IBasicClient {
|
||||
@Metadata
|
||||
Conformance getConformance();
|
||||
}
|
||||
|
||||
private final class ProfileResourceResolver implements IResourceResolver {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
|
||||
ourLog.debug("Loading template: {}", theName);
|
||||
if ("/".equals(theName)) {
|
||||
return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTester.html");
|
||||
}
|
||||
if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) {
|
||||
return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,899 +0,0 @@
|
|||
package ca.uhn.fhir.rest.server.tester;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR 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%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.TemplateProcessingParameters;
|
||||
import org.thymeleaf.context.WebContext;
|
||||
import org.thymeleaf.resourceresolver.IResourceResolver;
|
||||
import org.thymeleaf.standard.StandardDialect;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||
import ca.uhn.fhir.rest.gclient.StringParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public class RestfulTesterServlet extends HttpServlet {
|
||||
|
||||
private static final String PARAM_RESOURCE = "resource";
|
||||
|
||||
private static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
|
||||
|
||||
private static final boolean DEBUGMODE = true;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulTesterServlet.class);
|
||||
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
|
||||
private static final long serialVersionUID = 1L;
|
||||
private FhirContext myCtx;
|
||||
private LinkedHashMap<String, String> myIdToServerName = new LinkedHashMap<String, String>();
|
||||
private LinkedHashMap<String, String> myIdToServerBase = new LinkedHashMap<String, String>();
|
||||
private HashMap<String, String> myStaticResources;
|
||||
|
||||
private TemplateEngine myTemplateEngine;
|
||||
private Set<String> myFilterHeaders;
|
||||
|
||||
public RestfulTesterServlet() {
|
||||
myStaticResources = new HashMap<String, String>();
|
||||
myStaticResources.put("jquery-2.1.0.min.js", "text/javascript");
|
||||
myStaticResources.put("PublicTester.js", "text/javascript");
|
||||
myStaticResources.put("PublicTester.css", "text/css");
|
||||
myStaticResources.put("hapi_fhir_banner.png", "image/png");
|
||||
myStaticResources.put("hapi_fhir_banner_right.png", "image/png");
|
||||
myStaticResources.put("shCore.js", "text/javascript");
|
||||
myStaticResources.put("shBrushJScript.js", "text/javascript");
|
||||
myStaticResources.put("shBrushXml.js", "text/javascript");
|
||||
myStaticResources.put("shBrushPlain.js", "text/javascript");
|
||||
myStaticResources.put("shCore.css", "text/css");
|
||||
myStaticResources.put("shThemeDefault.css", "text/css");
|
||||
myStaticResources.put("json2.js", "text/javascript");
|
||||
myStaticResources.put("minify.json.js", "text/javascript");
|
||||
|
||||
myStaticResources.put("css/bootstrap.min.css", "text/css");
|
||||
myStaticResources.put("css/tester.css", "text/css");
|
||||
myStaticResources.put("img/hapi_fhir_banner.png", "image/png");
|
||||
myStaticResources.put("img/hapi_fhir_banner_right.png", "image/png");
|
||||
myStaticResources.put("js/RestfulTester.js", "text/javascript");
|
||||
|
||||
myStaticResources.put("js/bootstrap.min.js", "text/javascript");
|
||||
myStaticResources.put("js/jquery-2.1.0.min.js", "text/javascript");
|
||||
|
||||
myStaticResources.put("css/bootstrap-datetimepicker.min.css", "text/css");
|
||||
myStaticResources.put("js/bootstrap-datetimepicker.min.js", "text/javascript");
|
||||
myStaticResources.put("js/moment.min.js", "text/javascript");
|
||||
|
||||
myStaticResources.put("js/select2.min.js", "text/javascript");
|
||||
myStaticResources.put("css/select2.css", "text/css");
|
||||
myStaticResources.put("css/select2.png", "image/png");
|
||||
myStaticResources.put("css/select2x2.png", "image/png");
|
||||
myStaticResources.put("css/select2-spinner.gif", "image/gif");
|
||||
|
||||
myStaticResources.put("fonts/glyphicons-halflings-regular.eot", "application/octet-stream");
|
||||
myStaticResources.put("fonts/glyphicons-halflings-regular.svg", "application/octet-stream");
|
||||
myStaticResources.put("fonts/glyphicons-halflings-regular.ttf", "application/octet-stream");
|
||||
myStaticResources.put("fonts/glyphicons-halflings-regular.woff", "application/octet-stream");
|
||||
|
||||
myStaticResources.put("fa/css/font-awesome.css", "text/css");
|
||||
myStaticResources.put("fa/css/font-awesome.min.css", "text/css");
|
||||
myStaticResources.put("fa/fonts/fontawesome-webfont.eot", "application/octet-stream");
|
||||
myStaticResources.put("fa/fonts/fontawesome-webfont.svg", "application/octet-stream");
|
||||
myStaticResources.put("fa/fonts/fontawesome-webfont.ttf", "application/octet-stream");
|
||||
myStaticResources.put("fa/fonts/fontawesome-webfont.woff", "application/octet-stream");
|
||||
myStaticResources.put("fa/fonts/FontAwesome.otf", "application/octet-stream");
|
||||
myStaticResources.put("fa/less/bordered-pulled.less", "text/css");
|
||||
myStaticResources.put("fa/less/core.less", "text/css");
|
||||
myStaticResources.put("fa/less/fixed-width.less", "text/css");
|
||||
myStaticResources.put("fa/less/font-awesome.less", "text/css");
|
||||
myStaticResources.put("fa/less/icons.less", "text/css");
|
||||
myStaticResources.put("fa/less/larger.less", "text/css");
|
||||
myStaticResources.put("fa/less/list.less", "text/css");
|
||||
myStaticResources.put("fa/less/mixins.less", "text/css");
|
||||
myStaticResources.put("fa/less/path.less", "text/css");
|
||||
myStaticResources.put("fa/less/rotated-flipped.less", "text/css");
|
||||
myStaticResources.put("fa/less/spinning.less", "text/css");
|
||||
myStaticResources.put("fa/less/stacked.less", "text/css");
|
||||
myStaticResources.put("fa/less/variables.less", "text/css");
|
||||
myStaticResources.put("fa/scss/_bordered-pulled.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_core.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_fixed-width.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_icons.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_larger.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_list.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_mixins.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_path.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_rotated-flipped.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_spinning.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_stacked.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/_variables.scss", "text/css");
|
||||
myStaticResources.put("fa/scss/font-awesome.scss", "text/css");
|
||||
|
||||
myCtx = new FhirContext();
|
||||
}
|
||||
|
||||
public FhirContext getFhirContext() {
|
||||
return myCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig theConfig) throws ServletException {
|
||||
myTemplateEngine = new TemplateEngine();
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new ProfileResourceResolver());
|
||||
myTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
myTemplateEngine.setDialect(dialect);
|
||||
myTemplateEngine.initialize();
|
||||
}
|
||||
|
||||
public void addServerBase(String theId, String theDisplayName, String theServerBase) {
|
||||
Validate.notBlank(theId, "theId can not be blank");
|
||||
Validate.notBlank(theDisplayName, "theDisplayName can not be blank");
|
||||
Validate.notBlank(theServerBase, "theServerBase can not be blank");
|
||||
myIdToServerBase.put(theId, theServerBase);
|
||||
myIdToServerName.put(theId, theDisplayName);
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException {
|
||||
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
|
||||
if (def == null) {
|
||||
throw new ServletException("Invalid resourceName: " + resourceName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException {
|
||||
InputStream res = RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName);
|
||||
theResp.setContentType(theContentType);
|
||||
IOUtils.copy(res, theResp.getOutputStream());
|
||||
}
|
||||
|
||||
private enum ResultType {
|
||||
RESOURCE, BUNDLE, TAGLIST, NONE
|
||||
}
|
||||
|
||||
private void processAction(HttpServletRequest theReq, WebContext theContext, IGenericClient theClient, String theServerBase) {
|
||||
|
||||
GenericClient client = (GenericClient) theClient;
|
||||
client.setKeepResponses(true);
|
||||
ResultType returnsResource;
|
||||
long latency = 0;
|
||||
|
||||
String outcomeDescription = null;
|
||||
try {
|
||||
String method = theReq.getParameter("action");
|
||||
|
||||
String prettyParam = theReq.getParameter("pretty");
|
||||
if ("true".equals(prettyParam)) {
|
||||
client.setPrettyPrint(true);
|
||||
} else if ("false".equals(prettyParam)) {
|
||||
client.setPrettyPrint(false);
|
||||
}
|
||||
EncodingEnum encoding = getRequestEncoding(theReq);
|
||||
client.setEncoding(encoding);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
if ("home".equals(method)) {
|
||||
return;
|
||||
} else if ("conformance".equals(method)) {
|
||||
returnsResource = ResultType.RESOURCE;
|
||||
client.conformance();
|
||||
} else if ("read".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theContext.getVariables().put("errorMsg", "No ID specified");
|
||||
return;
|
||||
}
|
||||
returnsResource = ResultType.RESOURCE;
|
||||
|
||||
String versionId = StringUtils.defaultString(theReq.getParameter("vid"));
|
||||
if (StringUtils.isBlank(versionId)) {
|
||||
versionId = null;
|
||||
outcomeDescription = "Read Resource";
|
||||
} else {
|
||||
outcomeDescription = "VRead Resource";
|
||||
}
|
||||
|
||||
client.read(def.getImplementingClass(), new IdDt(def.getName(), id, versionId));
|
||||
|
||||
} else if ("get-tags".equals(method)) {
|
||||
|
||||
Class<? extends IResource> resType = null;
|
||||
if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
resType = def.getImplementingClass();
|
||||
String id = theReq.getParameter("resource-tags-id");
|
||||
if (isNotBlank(id)) {
|
||||
String vid = theReq.getParameter("resource-tags-vid");
|
||||
if (isNotBlank(vid)) {
|
||||
client.getTags().forResource(resType, id, vid).execute();
|
||||
} else {
|
||||
client.getTags().forResource(resType, id).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().forResource(resType).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().execute();
|
||||
}
|
||||
returnsResource = ResultType.TAGLIST;
|
||||
outcomeDescription = "Tag List";
|
||||
|
||||
} else if ("page".equals(method)) {
|
||||
|
||||
String url = defaultString(theReq.getParameter("page-url"));
|
||||
if (!url.startsWith(theServerBase)) {
|
||||
theContext.getVariables().put("errorMsg", "Invalid page URL: " + url);
|
||||
return;
|
||||
}
|
||||
|
||||
url = url.replace("&", "&");
|
||||
client.loadPage().url(url).execute();
|
||||
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
outcomeDescription = "Bundle Page";
|
||||
|
||||
} else if ("delete".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theContext.getVariables().put("errorMsg", "No ID specified");
|
||||
return;
|
||||
}
|
||||
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
outcomeDescription = "Delete Resource";
|
||||
|
||||
client.delete(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else if ("history-resource".equals(method) || "history-server".equals(method)) {
|
||||
String id = null;
|
||||
Class<? extends IResource> type = null; // def.getImplementingClass();
|
||||
if (!"history-server".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
type = def.getImplementingClass();
|
||||
id = StringUtils.defaultString(theReq.getParameter("resource-history-id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
if ("history-instance".equals(method)) {
|
||||
theContext.getVariables().put("errorMsg", "No ID specified");
|
||||
return;
|
||||
} else {
|
||||
id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DateTimeDt since = null;
|
||||
String sinceStr = theReq.getParameter("since");
|
||||
if (isNotBlank(sinceStr)) {
|
||||
since = new DateTimeDt(sinceStr);
|
||||
}
|
||||
|
||||
Integer limit = null;
|
||||
String limitStr = theReq.getParameter("limit");
|
||||
if (isNotBlank(limitStr)) {
|
||||
limit = Integer.parseInt(limitStr);
|
||||
}
|
||||
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
outcomeDescription = "Resource History";
|
||||
|
||||
client.history(type, id, since, limit);
|
||||
|
||||
} else if ("create".equals(method) || "validate".equals(method)) {
|
||||
boolean validate = "validate".equals(method);
|
||||
|
||||
String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body");
|
||||
if (isBlank(body)) {
|
||||
theContext.getVariables().put("errorMsg", "No message body specified");
|
||||
return;
|
||||
}
|
||||
|
||||
body = body.trim();
|
||||
IResource resource;
|
||||
try {
|
||||
if (body.startsWith("{")) {
|
||||
resource = myCtx.newJsonParser().parseResource(body);
|
||||
} else if (body.startsWith("<")) {
|
||||
resource = myCtx.newXmlParser().parseResource(body);
|
||||
} else {
|
||||
theContext.getVariables().put("errorMsg",
|
||||
"Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
|
||||
return;
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
ourLog.warn("Failed to parse resource", e);
|
||||
theContext.getVariables().put("errorMsg", "Failed to parse message body. Error was: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (validate) {
|
||||
client.validate(resource);
|
||||
outcomeDescription = "Validate Resource";
|
||||
} else {
|
||||
String id = theReq.getParameter("resource-create-id");
|
||||
if (isNotBlank(id)) {
|
||||
outcomeDescription = "Update Resource";
|
||||
client.update(id, resource);
|
||||
} else {
|
||||
outcomeDescription = "Create Resource";
|
||||
client.create(resource);
|
||||
}
|
||||
}
|
||||
returnsResource = ResultType.RESOURCE;
|
||||
|
||||
} else if ("search".equals(method)) {
|
||||
IUntypedQuery search = client.search();
|
||||
IQuery query;
|
||||
if (isNotBlank(theReq.getParameter("resource"))) {
|
||||
query = search.forResource(getResourceType(theReq).getImplementingClass());
|
||||
} else {
|
||||
query = search.forAllResources();
|
||||
}
|
||||
|
||||
outcomeDescription = "Search for Resources";
|
||||
|
||||
int paramIdx = -1;
|
||||
while (true) {
|
||||
paramIdx++;
|
||||
|
||||
String paramIdxString = Integer.toString(paramIdx);
|
||||
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query);
|
||||
if (!shouldContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE);
|
||||
if (incValues != null) {
|
||||
for (String next : incValues) {
|
||||
if (isNotBlank(next)) {
|
||||
query.include(new Include(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String limit = theReq.getParameter("resource-search-limit");
|
||||
if (isNotBlank(limit)) {
|
||||
if (!limit.matches("[0-9]+")) {
|
||||
theContext.getVariables().put("errorMsg", "Search limit must be a numeric value.");
|
||||
return;
|
||||
}
|
||||
query.limitTo(Integer.parseInt(limit));
|
||||
}
|
||||
|
||||
query.execute();
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
|
||||
} else {
|
||||
theContext.getVariables().put("errorMsg", "Invalid action: " + method);
|
||||
return;
|
||||
}
|
||||
|
||||
latency = System.currentTimeMillis() - start;
|
||||
} catch (DataFormatException e) {
|
||||
ourLog.error("Failed to invoke method", e);
|
||||
returnsResource = ResultType.NONE;
|
||||
} catch (BaseServerResponseException e) {
|
||||
ourLog.error("Failed to invoke method", e);
|
||||
returnsResource = ResultType.NONE;
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
returnsResource = ResultType.NONE;
|
||||
}
|
||||
|
||||
try {
|
||||
HttpRequestBase lastRequest = client.getLastRequest();
|
||||
String requestBody = null;
|
||||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null;
|
||||
String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null;
|
||||
String resultBody = client.getLastResponseBody();
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
if (entity.isRepeatable()) {
|
||||
requestBody = IOUtils.toString(entity.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse lastResponse = client.getLastResponse();
|
||||
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
String narrativeString = "";
|
||||
|
||||
StringBuilder resultDescription = new StringBuilder();
|
||||
Bundle bundle = null;
|
||||
|
||||
if (ctEnum == null) {
|
||||
resultDescription.append("Non-FHIR response");
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
if (returnsResource == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
resultDescription.append("JSON resource");
|
||||
} else if (returnsResource == ResultType.BUNDLE) {
|
||||
resultDescription.append("JSON bundle");
|
||||
bundle = myCtx.newJsonParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
if (returnsResource == ResultType.RESOURCE) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
resultDescription.append("XML resource");
|
||||
} else if (returnsResource == ResultType.BUNDLE) {
|
||||
resultDescription.append("XML bundle");
|
||||
bundle = myCtx.newXmlParser().parseBundle(resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
resultDescription.append(" (").append(resultBody.length() + " bytes)");
|
||||
|
||||
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
|
||||
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
|
||||
|
||||
theContext.setVariable("outcomeDescription", outcomeDescription);
|
||||
theContext.setVariable("resultDescription", resultDescription.toString());
|
||||
theContext.setVariable("action", action);
|
||||
theContext.setVariable("bundle", bundle);
|
||||
theContext.setVariable("resultStatus", resultStatus);
|
||||
theContext.setVariable("requestUrl", requestUrl);
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theContext.setVariable("requestBody", requestBodyText);
|
||||
String resultBodyText = format(resultBody, ctEnum);
|
||||
theContext.setVariable("resultBody", resultBodyText);
|
||||
theContext.setVariable("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theContext.setVariable("requestHeaders", requestHeaders);
|
||||
theContext.setVariable("responseHeaders", responseHeaders);
|
||||
theContext.setVariable("narrative", narrativeString);
|
||||
theContext.setVariable("latencyMs", latency);
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theContext.getVariables().put("errorMsg", "Error during processing: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery) {
|
||||
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
||||
if (isBlank(nextName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier"));
|
||||
|
||||
String nextType = theReq.getParameter("param." + paramIdxString + ".type");
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
b.append(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)));
|
||||
}
|
||||
|
||||
String paramValue = b.toString();
|
||||
if (isBlank(paramValue)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("token".equals(nextType)) {
|
||||
if (paramValue.length() < 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if ("xml".equals(theReq.getParameter("encoding"))) {
|
||||
// query.encodedXml();
|
||||
// }else if ("json".equals(theReq.getParameter("encoding"))) {
|
||||
// query.encodedJson();
|
||||
// }
|
||||
|
||||
theQuery.where(new StringParam(nextName + nextQualifier).matches().value(paramValue));
|
||||
|
||||
if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) {
|
||||
handleSearchParam(paramIdxString + ".0", theReq, theQuery);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
if (theEncodingEnum == EncodingEnum.JSON) {
|
||||
|
||||
boolean inValue = false;
|
||||
boolean inQuote = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
inValue = true;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean inQuote = false;
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private EncodingEnum getRequestEncoding(HttpServletRequest theReq) {
|
||||
EncodingEnum encoding;
|
||||
if ("xml".equals(theReq.getParameter("encoding"))) {
|
||||
encoding = EncodingEnum.XML;
|
||||
} else if ("json".equals(theReq.getParameter("encoding"))) {
|
||||
encoding = (EncodingEnum.JSON);
|
||||
} else {
|
||||
encoding = null;
|
||||
}
|
||||
return encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
try {
|
||||
ourLog.trace("RequestURI: {}", theReq.getPathInfo());
|
||||
|
||||
String resName = theReq.getPathInfo().substring(1);
|
||||
if (myStaticResources.containsKey(resName)) {
|
||||
streamResponse(resName, myStaticResources.get(resName), theResp);
|
||||
return;
|
||||
}
|
||||
|
||||
String serverId = theReq.getParameter("server-id");
|
||||
String serverBase;
|
||||
String serverName;
|
||||
if (isBlank(serverId) && !myIdToServerBase.containsKey(serverId)) {
|
||||
serverBase = myIdToServerBase.entrySet().iterator().next().getValue();
|
||||
serverName = myIdToServerName.entrySet().iterator().next().getValue();
|
||||
} else {
|
||||
serverBase = myIdToServerBase.get(serverId);
|
||||
serverName = myIdToServerName.get(serverId);
|
||||
}
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient(serverBase);
|
||||
|
||||
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = client.conformance();
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to load conformance statement", e);
|
||||
ctx.getVariables().put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
|
||||
conformance = new Conformance();
|
||||
}
|
||||
|
||||
Map<String, Number> resourceCounts = new HashMap<String, Number>();
|
||||
long total = 0;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (RestResource nextResource : nextRest.getResource()) {
|
||||
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (exts != null && exts.size() > 0) {
|
||||
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
|
||||
resourceCounts.put(nextResource.getType().getValue(), nextCount);
|
||||
total += nextCount.longValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (total > 0) {
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
Collections.sort(nextRest.getResource(), new Comparator<RestResource>() {
|
||||
@Override
|
||||
public int compare(RestResource theO1, RestResource theO2) {
|
||||
DecimalDt count1 = new DecimalDt();
|
||||
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count1exts != null && count1exts.size() > 0) {
|
||||
count1 = (DecimalDt) count1exts.get(0).getValue();
|
||||
}
|
||||
DecimalDt count2 = new DecimalDt();
|
||||
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
|
||||
if (count2exts != null && count2exts.size() > 0) {
|
||||
count2 = (DecimalDt) count2exts.get(0).getValue();
|
||||
}
|
||||
int retVal = count2.compareTo(count1);
|
||||
if (retVal == 0) {
|
||||
retVal = theO1.getType().getValue().compareTo(theO2.getType().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.setVariable("serverId", serverId);
|
||||
ctx.setVariable("resourceCounts", resourceCounts);
|
||||
ctx.setVariable("conf", conformance);
|
||||
ctx.setVariable("base", serverBase);
|
||||
ctx.setVariable("baseName", serverName);
|
||||
ctx.setVariable("serverEntries", myIdToServerName.entrySet());
|
||||
String resourceName = defaultString(theReq.getParameter(PARAM_RESOURCE));
|
||||
ctx.setVariable("resourceName", resourceName);
|
||||
ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance));
|
||||
addStandardVariables(ctx, theReq.getParameterMap());
|
||||
|
||||
if (isNotBlank(theReq.getParameter("action"))) {
|
||||
processAction(theReq, ctx, client, serverBase);
|
||||
}
|
||||
|
||||
if (isNotBlank(resourceName)) {
|
||||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
|
||||
TreeSet<String> includes = new TreeSet<String>();
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (RestResource nextRes : nextRest.getResource()) {
|
||||
if (nextRes.getType().getValue().equals(resourceName)) {
|
||||
for (StringDt next : nextRes.getSearchInclude()) {
|
||||
if (next.isEmpty() == false) {
|
||||
includes.add(next.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.setVariable("includes", includes);
|
||||
|
||||
if (isNotBlank(theReq.getParameter("update-id"))) {
|
||||
String updateId = theReq.getParameter("update-id");
|
||||
String updateVid = defaultIfEmpty(theReq.getParameter("update-vid"), null);
|
||||
IResource updateResource = client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid));
|
||||
EncodingEnum encoding = getRequestEncoding(theReq);
|
||||
if (encoding == null) {
|
||||
encoding = EncodingEnum.XML;
|
||||
}
|
||||
String updateResourceString = encoding.newParser(myCtx).setPrettyPrint(true).encodeResourceToString(updateResource);
|
||||
ctx.setVariable("updateResource", updateResourceString);
|
||||
ctx.setVariable("updateResourceId", updateId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
theResp.setContentType("text/html");
|
||||
theResp.setCharacterEncoding("UTF-8");
|
||||
|
||||
myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to respond", e);
|
||||
theResp.sendError(500, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void addStandardVariables(WebContext theCtx, Map<String, String[]> theParameterMap) {
|
||||
addStandardVariable(theCtx, theParameterMap, "encoding");
|
||||
addStandardVariable(theCtx, theParameterMap, "pretty");
|
||||
}
|
||||
|
||||
private void addStandardVariable(WebContext theCtx, Map<String, String[]> theParameterMap, String key) {
|
||||
if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) {
|
||||
theCtx.setVariable(key, theParameterMap.get(key)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
|
||||
return theAllHeaders;
|
||||
}
|
||||
ArrayList<Header> retVal = new ArrayList<Header>();
|
||||
for (Header next : theAllHeaders) {
|
||||
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
return retVal.toArray(new Header[retVal.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user. This can be used, for instance, to filter out "Authorization" headers. Note that
|
||||
* names are not case sensitive.
|
||||
*/
|
||||
public void setFilterHeaders(String... theHeaderNames) {
|
||||
myFilterHeaders = new HashSet<String>();
|
||||
if (theHeaderNames != null) {
|
||||
for (String next : theHeaderNames) {
|
||||
myFilterHeaders.add(next.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private final class ProfileResourceResolver implements IResourceResolver {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
|
||||
ourLog.debug("Loading template: {}", theName);
|
||||
if ("/".equals(theName)) {
|
||||
return RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/RestfulTester.html");
|
||||
}
|
||||
if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) {
|
||||
return RestfulTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
A.selectedFunctionLink {
|
||||
text-decoration: none;
|
||||
background: #E0FFE0;
|
||||
border: 1px #80C080 solid;
|
||||
color: #005000;
|
||||
}
|
||||
|
||||
BODY {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
SPAN.headerName {
|
||||
color: #505080;
|
||||
}
|
||||
|
||||
SPAN.headerValue {
|
||||
color: #70A070;
|
||||
}
|
||||
|
||||
DIV.bodyHeaderBlock {
|
||||
background-color: #E0E0E0;
|
||||
margin-top: 5px;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
DIV.textareaWrapper {
|
||||
position:relative;
|
||||
height:100%;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
DIV.textareaWrapper TEXTAREA {
|
||||
width:95%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
TABLE.propertyTable {
|
||||
margin-left: 4px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
TD.propertyKeyCell {
|
||||
background-color: #E0E0FF;
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
TD.testerNameCell {
|
||||
background-color: #E0FFE0;
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
font-size: 0.85em !important;
|
||||
min-height: 1.4em;
|
||||
max-width: 800px;
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<!--/*
|
||||
************************************************************
|
||||
This file is a Thymeleaf template for the
|
||||
************************************************************
|
||||
*/-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
var serverBase = "<th:block th:text="${base}"/>";
|
||||
var conformance = <th:block th:utext="${jsonEncodedConf}"/>;
|
||||
</script>
|
||||
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
|
||||
<script type="text/javascript" src="PublicTester.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="PublicTester.css"/>
|
||||
<meta charset="UTF-8"/>
|
||||
<script type="text/javascript" src="json2.js"></script>
|
||||
<script type="text/javascript" src="minify.json.js"></script>
|
||||
</head>
|
||||
<body onload="selectResourceType();">
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
<td align="left"><img src="hapi_fhir_banner.png"/></td>
|
||||
<td align="right"><img src="hapi_fhir_banner_right.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="bodyHeaderBlock">
|
||||
This is a RESTful server tester, which can be used to send requests to, and receive responses
|
||||
from the server at the following URL:<br/>
|
||||
<a href="http://foo.com/fhir" th:href="${base}"><th:block th:text="${base}">http://foo.com/fhir</th:block></a>
|
||||
</div>
|
||||
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">
|
||||
<tr>
|
||||
<td width="29%" valign="top">
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Software Details
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Software</td>
|
||||
<td th:text="${conf.software.name}">HAPI Restful Server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Version</td>
|
||||
<td th:text="${conf.software.version}">1.1.1</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Request Configuration
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Encoding</td>
|
||||
<td>
|
||||
<select id="configEncoding" th:switch="${configEncoding}">
|
||||
<th:block th:case="'xml'">
|
||||
<option value="">(default)</option>
|
||||
<option value="xml" selected="selected">XML</option>
|
||||
<option value="json">JSON</option>
|
||||
</th:block>
|
||||
<th:block th:case="'json'">
|
||||
<option value="">(default)</option>
|
||||
<option value="xml">XML</option>
|
||||
<option value="json" selected="selected">JSON</option>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<option value="" selected="selected">(default)</option>
|
||||
<option value="xml">XML</option>
|
||||
<option value="json">JSON</option>
|
||||
</th:block>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Pretty Printing</td>
|
||||
<td th:switch="${configPretty}">
|
||||
<input th:case="'false'" type="checkbox" id="configPretty"/>
|
||||
<input th:case="*" type="checkbox" id="configPretty" checked="checked"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
|
||||
<td width="1%"/>
|
||||
|
||||
<td width="70%" valign="top">
|
||||
<th:block th:each="rest : ${conf.rest}">
|
||||
<div class="bodyHeaderBlock">
|
||||
RESTful Server
|
||||
</div>
|
||||
<table border="0" th:id="systemExpando" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Supports operations:</td>
|
||||
<td valign="top">
|
||||
<a th:onclick="'displayConformance(this, \'systemExpando\'); return false;'" href="#">conformance</a>
|
||||
<th:span th:each="operation : ${rest.operation}">
|
||||
<th:block th:switch="${operation.code.value}">
|
||||
<span th:case="*" th:text="${operation.code.value}"/>
|
||||
</th:block>
|
||||
</th:span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Resources
|
||||
</div>
|
||||
|
||||
<table border="0" th:id="systemExpando" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Select Resource:</td>
|
||||
<td valign="top">
|
||||
<select id="configResource" onchange="selectResourceType();">
|
||||
<th:block th:each="resource, resIterStat : ${rest.resource}">
|
||||
<option th:value="${resource.type.valueAsString}" th:text="${resource.type.valueAsString}"/>
|
||||
</th:block>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}" th:id="'res' + ${resource.type.valueAsString}" class="resourceTypeContainer" style="display:none;">
|
||||
<div class="bodyHeaderBlock">
|
||||
Resource: <th:block th:text="${resource.type.valueAsString}"/>
|
||||
</div>
|
||||
<table border="0" th:id="${expandoId}" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Supports operations:</td>
|
||||
<td valign="top">
|
||||
<th:span th:each="operation : ${resource.operation}">
|
||||
<th:block th:switch="${operation.code.value}">
|
||||
<a th:case="'read'" href="#" th:onclick="'displayRead(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">read</a>
|
||||
<a th:case="'vread'" href="#" th:onclick="'displayVRead(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">vread</a>
|
||||
<a th:case="'search-type'" href="#" th:onclick="'displaySearchType(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">search-type</a>
|
||||
<a th:case="'create'" href="#" th:onclick="'displayCreate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">create</a>
|
||||
<a th:case="'update'" href="#" th:onclick="'displayUpdate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">update</a>
|
||||
<a th:case="'validate'" href="#" th:onclick="'displayValidate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">validate</a>
|
||||
<a th:case="'delete'" href="#" th:onclick="'displayDelete(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">delete</a>
|
||||
<a th:case="'history-instance'" href="#" th:onclick="'displayHistoryInstance(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">history-instance</a>
|
||||
<span th:case="*" href="#" th:text="${operation.code.value}"/>
|
||||
</th:block>
|
||||
</th:span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</th:block>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,360 +0,0 @@
|
|||
var uniqueIdSeed = 1;
|
||||
|
||||
function addConfigElementsToForm(theForm) {
|
||||
var encoding = document.getElementById('configEncoding');
|
||||
var pretty = document.getElementById('configPretty');
|
||||
|
||||
var newEncoding = document.createElement("input");
|
||||
newEncoding.type='hidden';
|
||||
newEncoding.name='configEncoding';
|
||||
newEncoding.value = encoding.value;
|
||||
theForm.appendChild(newEncoding);
|
||||
|
||||
var newPretty = document.createElement("input");
|
||||
newPretty.type='hidden';
|
||||
newPretty.name='configPretty';
|
||||
if (pretty.checked) {
|
||||
newPretty.value='on';
|
||||
}
|
||||
newPretty.checked = pretty.checked;
|
||||
theForm.appendChild(newPretty);
|
||||
}
|
||||
|
||||
function appendSearchParam(formElement, searchParam) {
|
||||
var inputId = newUniqueId();
|
||||
if (searchParam.type && searchParam.type == 'token') {
|
||||
formElement.append(
|
||||
$('<input />', { name: 'param.token.1.' + searchParam.name, placeholder: 'system/namespace', type: 'text', id: inputId }),
|
||||
$('<input />', { name: 'param.token.2.' + searchParam.name, placeholder: 'value', type: 'text', id: inputId }),
|
||||
$('<label for="' + inputId + '">' + searchParam.name + '</input>')
|
||||
);
|
||||
} else {
|
||||
formElement.append(
|
||||
$('<input />', { name: 'param.string.' + searchParam.name, placeholder: searchParam.name, type: 'text', id: inputId }),
|
||||
$('<label for="' + inputId + '">' + searchParam.name + '</input>')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function appendSearchParamExtension(extension) {
|
||||
|
||||
}
|
||||
|
||||
/** Hide any currently displayed tester form */
|
||||
function clearCurrentForm(postCompleteFunction) {
|
||||
/* $('.testerNameRow').each().fadeOut(500).promise().then(function(){
|
||||
if (postCompleteFunction != null) {
|
||||
$('.testerNameRow').each().remove();
|
||||
postCompleteFunction();
|
||||
postCompleteFunction = null;
|
||||
}
|
||||
}); */
|
||||
var current = $('.testerNameRow');
|
||||
if (current.length == 0) {
|
||||
postCompleteFunction();
|
||||
} else {
|
||||
current.first().fadeOut(300, function() {
|
||||
current.first().remove();
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayConformance(button, expandoTr) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Conformance</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'conformance', type: 'hidden' }),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
function minifyTextarea(self) {
|
||||
var value = value;
|
||||
value = value.replace(/[\u00A0\u1680\u180e\u2000-\u2009\u200a\u200b\u202f\u205f\u3000]/g,' ');
|
||||
//value = JSON.minify(value);
|
||||
$('#textarea').val(value);
|
||||
return addConfigElementsToForm(self);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayCreate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Create</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'create', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<div class="textareaWrapper">').append(
|
||||
$('<textarea />', { id: 'textarea', name: 'resource', rows: 10, style: 'white-space: nowrap;' })
|
||||
),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'delete' method */
|
||||
function displayDelete(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Delete</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'delete', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayUpdate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Update</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'update', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<textarea />', { id: 'textarea', name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'search' method */
|
||||
function displaySearchType(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
|
||||
// Add a search form for the default (no parameter) search, which should
|
||||
// return all resources of the given type
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Search by Type</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'searchType', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<span>All Resources of Type</span><br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Loop through each supported search parameter and add a search form for it
|
||||
conformance.rest.forEach(function(rest){
|
||||
rest.resource.forEach(function(restResource){
|
||||
if (restResource.type == resourceName) {
|
||||
if (restResource.searchParam) {
|
||||
var paramIndex = 0;
|
||||
restResource.searchParam.forEach(function(searchParam){
|
||||
var formElement = $('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" });
|
||||
formElement.append(
|
||||
$('<input />', { name: 'method', value: 'searchType', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' })
|
||||
)
|
||||
if (searchParam.documentation && searchParam.documentation.length > 0) {
|
||||
formElement.append(
|
||||
$('<span>' + searchParam.documentation + '<br /></span>')
|
||||
);
|
||||
}
|
||||
|
||||
appendSearchParam(formElement, searchParam);
|
||||
|
||||
// http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam
|
||||
if (restResource._searchParam && restResource._searchParam[paramIndex] != null) {
|
||||
if (restResource._searchParam[paramIndex].extension) {
|
||||
appendSearchParamExtension(restResource._searchParam[paramIndex].extension);
|
||||
}
|
||||
}
|
||||
|
||||
formElement.append(
|
||||
$('<br />')
|
||||
);
|
||||
formElement.append(
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
);
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Search by Type</td>'),
|
||||
$('<td />').append(
|
||||
formElement
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
paramIndex++;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'instance-history' method */
|
||||
function displayHistoryInstance(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'history-instance', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayRead(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'read', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayValidate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Validate</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'validate', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<textarea />', { id: 'textarea', name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayVRead(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'vread', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<input />', { name: 'versionid', placeholder: 'Version ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
function highlightSelectedLink(button) {
|
||||
$('.selectedFunctionLink').each(function() {
|
||||
$(this).removeClass('selectedFunctionLink');
|
||||
});
|
||||
button.className = 'selectedFunctionLink';
|
||||
}
|
||||
|
||||
/** Generate a unique ID */
|
||||
function newUniqueId() {
|
||||
return "uid" + uniqueIdSeed++;
|
||||
}
|
||||
|
||||
function selectResourceType() {
|
||||
$('.resourceTypeContainer').each(function() {
|
||||
$(this).hide();
|
||||
});
|
||||
var cr = document.getElementById('configResource');
|
||||
var selected = cr.options[cr.selectedIndex].value;
|
||||
$('#res' + selected).show();
|
||||
}
|
||||
|
||||
/** Show a newly created tester form */
|
||||
function showNewForm() {
|
||||
var time = 0;
|
||||
$('.testerNameRow').each(function() {
|
||||
$(this).delay(time).fadeIn(200);
|
||||
time += 200;
|
||||
});
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<!--/*
|
||||
************************************************************
|
||||
This file is a Thymeleaf template for the
|
||||
************************************************************
|
||||
*/-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script src="shCore.js" type="text/javascript"></script>
|
||||
<script src="shBrushJScript.js" type="text/javascript"></script>
|
||||
<script src="shBrushXml.js" type="text/javascript"></script>
|
||||
<script src="shBrushPlain.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shCore.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="shThemeDefault.css"/>
|
||||
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
|
||||
<script type="text/javascript" src="PublicTester.js"></script>
|
||||
<script type="text/javascript">
|
||||
var serverBase = "<th:block th:utext="${base}"/>";
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="PublicTester.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
<td align="left"><img src="hapi_fhir_banner.png"/></td>
|
||||
<td align="right"><img src="hapi_fhir_banner_right.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="bodyHeaderBlock">
|
||||
Executed invocation against FHIR RESTful Server in
|
||||
<th:block th:text="${latencyMs} + 'ms'"/>
|
||||
</div>
|
||||
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">
|
||||
<tr>
|
||||
<td width="29%" valign="top">
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Actions
|
||||
</div>
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Something</td>
|
||||
<td>Something</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
|
||||
<td width="1%"/>
|
||||
|
||||
<td width="70%" valign="top">
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Request
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Action</td>
|
||||
<td th:text="${action}">GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="propertyKeyCell">URL</td>
|
||||
<td>
|
||||
<a href="http://foo.com/fhir" th:href="${requestUrl}"><th:block th:text="${requestUrl}">http://foo.com/fhir</th:block></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr > <!-- th:if="${not #lists.isEmpty(requestHeaders)}" -->
|
||||
<td valign="top" class="propertyKeyCell">Headers</td>
|
||||
<td valign="top">
|
||||
<div th:each="header : ${requestHeaders}">
|
||||
<span class="headerName" th:text="${header.name} + ': '"/>
|
||||
<span class="headerValue" th:text="${header.value}"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${requestBody} != null">
|
||||
<td valign="top" class="propertyKeyCell">Request Body</td>
|
||||
<td valign="top"><pre th:text="${requestBody}" th:class="${requestSyntaxHighlighterClass}">{}</pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Response
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Status</td>
|
||||
<td valign="top" th:text="${resultStatus}">HTTP 200 OK</td>
|
||||
</tr>
|
||||
<tr > <!-- th:if="${not #lists.isEmpty(responseHeaders)}" -->
|
||||
<td valign="top" class="propertyKeyCell">Headers</td>
|
||||
<td valign="top">
|
||||
<div th:each="header : ${responseHeaders}">
|
||||
<span class="headerName" th:text="${header.name} + ': '"/>
|
||||
<span class="headerValue" th:text="${header.value}"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${!#strings.isEmpty(resultBody)}">
|
||||
<td valign="top" class="propertyKeyCell">Result Body</td>
|
||||
<td valign="top"><pre th:text="${resultBody}" th:class="${resultSyntaxHighlighterClass}">{}</pre></td>
|
||||
</tr>
|
||||
<tr th:if="${!#strings.isEmpty(narrative)}">
|
||||
<td valign="top" class="propertyKeyCell">Result Narrative</td>
|
||||
<td valign="top" th:utext="${narrative}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter.all();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.0 KiB |
File diff suppressed because one or more lines are too long
|
@ -1,11 +0,0 @@
|
|||
/*!
|
||||
http://www.JSON.org/json2.js
|
||||
2009-09-29
|
||||
|
||||
Public Domain.
|
||||
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
See http://www.JSON.org/js.html
|
||||
*/
|
||||
if(!this.JSON){this.JSON={}}(function(){function l(c){return c<10?'0'+c:c}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(c){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+l(this.getUTCMonth()+1)+'-'+l(this.getUTCDate())+'T'+l(this.getUTCHours())+':'+l(this.getUTCMinutes())+':'+l(this.getUTCSeconds())+'Z':null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(c){return this.valueOf()}}var o=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,h,m,r={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},j;function q(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(c){var f=r[c];return typeof f==='string'?f:'\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function n(c,f){var a,e,d,i,k=h,g,b=f[c];if(b&&typeof b==='object'&&typeof b.toJSON==='function'){b=b.toJSON(c)}if(typeof j==='function'){b=j.call(f,c,b)}switch(typeof b){case'string':return q(b);case'number':return isFinite(b)?String(b):'null';case'boolean':case'null':return String(b);case'object':if(!b){return'null'}h+=m;g=[];if(Object.prototype.toString.apply(b)==='[object Array]'){i=b.length;for(a=0;a<i;a+=1){g[a]=n(a,b)||'null'}d=g.length===0?'[]':h?'[\n'+h+g.join(',\n'+h)+'\n'+k+']':'['+g.join(',')+']';h=k;return d}if(j&&typeof j==='object'){i=j.length;for(a=0;a<i;a+=1){e=j[a];if(typeof e==='string'){d=n(e,b);if(d){g.push(q(e)+(h?': ':':')+d)}}}}else{for(e in b){if(Object.hasOwnProperty.call(b,e)){d=n(e,b);if(d){g.push(q(e)+(h?': ':':')+d)}}}}d=g.length===0?'{}':h?'{\n'+h+g.join(',\n'+h)+'\n'+k+'}':'{'+g.join(',')+'}';h=k;return d}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(c,f,a){var e;h='';m='';if(typeof a==='number'){for(e=0;e<a;e+=1){m+=' '}}else if(typeof a==='string'){m=a}j=f;if(f&&typeof f!=='function'&&(typeof f!=='object'||typeof f.length!=='number')){throw new Error('JSON.stringify');}return n('',{'':c})}}if(typeof JSON.parse!=='function'){JSON.parse=function(i,k){var g;function b(c,f){var a,e,d=c[f];if(d&&typeof d==='object'){for(a in d){if(Object.hasOwnProperty.call(d,a)){e=b(d,a);if(e!==undefined){d[a]=e}else{delete d[a]}}}}return k.call(c,f,d)}o.lastIndex=0;if(o.test(i)){i=i.replace(o,function(c){return'\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(i.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){g=eval('('+i+')');return typeof k==='function'?b({'':g},''):g}throw new SyntaxError('JSON.parse');}}}());
|
|
@ -1,61 +0,0 @@
|
|||
/*! JSON.minify()
|
||||
v0.1 (c) Kyle Simpson
|
||||
MIT License
|
||||
*/
|
||||
|
||||
(function(global){
|
||||
if (typeof global.JSON == "undefined" || !global.JSON) {
|
||||
global.JSON = {};
|
||||
}
|
||||
|
||||
global.JSON.minify = function(json) {
|
||||
|
||||
var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
|
||||
in_string = false,
|
||||
in_multiline_comment = false,
|
||||
in_singleline_comment = false,
|
||||
tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc
|
||||
;
|
||||
|
||||
tokenizer.lastIndex = 0;
|
||||
|
||||
while (tmp = tokenizer.exec(json)) {
|
||||
lc = RegExp.leftContext;
|
||||
rc = RegExp.rightContext;
|
||||
if (!in_multiline_comment && !in_singleline_comment) {
|
||||
tmp2 = lc.substring(from);
|
||||
if (!in_string) {
|
||||
tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
|
||||
}
|
||||
new_str[ns++] = tmp2;
|
||||
}
|
||||
from = tokenizer.lastIndex;
|
||||
|
||||
if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
|
||||
tmp2 = lc.match(/(\\)*$/);
|
||||
if (!in_string || !tmp2 || (tmp2[0].length % 2) == 0) { // start of string with ", or unescaped " character found to end string
|
||||
in_string = !in_string;
|
||||
}
|
||||
from--; // include " character in next catch
|
||||
rc = json.substring(from);
|
||||
}
|
||||
else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
|
||||
in_multiline_comment = true;
|
||||
}
|
||||
else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
|
||||
in_multiline_comment = false;
|
||||
}
|
||||
else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
|
||||
in_singleline_comment = true;
|
||||
}
|
||||
else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
|
||||
in_singleline_comment = false;
|
||||
}
|
||||
else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
|
||||
new_str[ns++] = tmp[0];
|
||||
}
|
||||
}
|
||||
new_str[ns++] = rc;
|
||||
return new_str.join("");
|
||||
};
|
||||
})(this);
|
|
@ -1,52 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
var keywords = 'break case catch continue ' +
|
||||
'default delete do else false ' +
|
||||
'for function if in instanceof ' +
|
||||
'new null return super switch ' +
|
||||
'this throw true try typeof var while with'
|
||||
;
|
||||
|
||||
var r = SyntaxHighlighter.regexLib;
|
||||
|
||||
this.regexList = [
|
||||
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
||||
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
||||
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
||||
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
||||
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
|
||||
];
|
||||
|
||||
this.forHtmlScript(r.scriptScriptTags);
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['js', 'jscript', 'javascript'];
|
||||
|
||||
SyntaxHighlighter.brushes.JScript = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['text', 'plain'];
|
||||
|
||||
SyntaxHighlighter.brushes.Plain = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
function process(match, regexInfo)
|
||||
{
|
||||
var constructor = SyntaxHighlighter.Match,
|
||||
code = match[0],
|
||||
tag = new XRegExp('(<|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
|
||||
result = []
|
||||
;
|
||||
|
||||
if (match.attributes != null)
|
||||
{
|
||||
var attributes,
|
||||
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
|
||||
'\\s*=\\s*' +
|
||||
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
||||
'xg');
|
||||
|
||||
while ((attributes = regex.exec(code)) != null)
|
||||
{
|
||||
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
|
||||
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null)
|
||||
result.push(
|
||||
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.regexList = [
|
||||
{ regex: new XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{ regex: new XRegExp('(<|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
|
||||
];
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
|
||||
|
||||
SyntaxHighlighter.brushes.Xml = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
|
@ -1,226 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter a,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody,
|
||||
.syntaxhighlighter table thead,
|
||||
.syntaxhighlighter table caption,
|
||||
.syntaxhighlighter textarea {
|
||||
-moz-border-radius: 0 0 0 0 !important;
|
||||
-webkit-border-radius: 0 0 0 0 !important;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
bottom: auto !important;
|
||||
float: none !important;
|
||||
height: auto !important;
|
||||
left: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
text-align: left !important;
|
||||
top: auto !important;
|
||||
vertical-align: baseline !important;
|
||||
width: auto !important;
|
||||
box-sizing: content-box !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 1em !important;
|
||||
min-height: inherit !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
width: 100% !important;
|
||||
margin: 1em 0 1em 0 !important;
|
||||
position: relative !important;
|
||||
overflow: auto !important;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.source {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.syntaxhighlighter .bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter .italic {
|
||||
font-style: italic !important;
|
||||
}
|
||||
.syntaxhighlighter .line {
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
text-align: left !important;
|
||||
padding: .5em 0 0.5em 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container {
|
||||
position: relative !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container textarea {
|
||||
box-sizing: border-box !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border: none !important;
|
||||
background: white !important;
|
||||
padding-left: 1em !important;
|
||||
overflow: hidden !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table td.gutter .line {
|
||||
text-align: right !important;
|
||||
padding: 0 0.5em 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .line {
|
||||
padding: 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
|
||||
padding-left: 0em !important;
|
||||
}
|
||||
.syntaxhighlighter.show {
|
||||
display: block !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed table {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
padding: 0.1em 0.8em 0em 0.8em !important;
|
||||
font-size: 1em !important;
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span {
|
||||
display: inline !important;
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a {
|
||||
padding: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
position: absolute !important;
|
||||
right: 1px !important;
|
||||
top: 1px !important;
|
||||
width: 11px !important;
|
||||
height: 11px !important;
|
||||
font-size: 10px !important;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar span.title {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
text-decoration: none !important;
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a.expandSource {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.ie {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 0 1px 0 !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar {
|
||||
line-height: 8px !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar a {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
|
||||
background: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .number {
|
||||
color: #bbbbbb !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .keyword {
|
||||
color: #006699 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .script {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
|
||||
color: black !important;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,328 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter a,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody,
|
||||
.syntaxhighlighter table thead,
|
||||
.syntaxhighlighter table caption,
|
||||
.syntaxhighlighter textarea {
|
||||
-moz-border-radius: 0 0 0 0 !important;
|
||||
-webkit-border-radius: 0 0 0 0 !important;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
bottom: auto !important;
|
||||
float: none !important;
|
||||
height: auto !important;
|
||||
left: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
text-align: left !important;
|
||||
top: auto !important;
|
||||
vertical-align: baseline !important;
|
||||
width: auto !important;
|
||||
box-sizing: content-box !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 1em !important;
|
||||
min-height: inherit !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
width: 100% !important;
|
||||
margin: 1em 0 1em 0 !important;
|
||||
position: relative !important;
|
||||
overflow: auto !important;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.source {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.syntaxhighlighter .bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter .italic {
|
||||
font-style: italic !important;
|
||||
}
|
||||
.syntaxhighlighter .line {
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
text-align: left !important;
|
||||
padding: .5em 0 0.5em 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container {
|
||||
position: relative !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container textarea {
|
||||
box-sizing: border-box !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border: none !important;
|
||||
background: white !important;
|
||||
padding-left: 1em !important;
|
||||
overflow: hidden !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table td.gutter .line {
|
||||
text-align: right !important;
|
||||
padding: 0 0.5em 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .line {
|
||||
padding: 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
|
||||
padding-left: 0em !important;
|
||||
}
|
||||
.syntaxhighlighter.show {
|
||||
display: block !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed table {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
padding: 0.1em 0.8em 0em 0.8em !important;
|
||||
font-size: 1em !important;
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span {
|
||||
display: inline !important;
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a {
|
||||
padding: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
position: absolute !important;
|
||||
right: 1px !important;
|
||||
top: 1px !important;
|
||||
width: 11px !important;
|
||||
height: 11px !important;
|
||||
font-size: 10px !important;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar span.title {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
text-decoration: none !important;
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a.expandSource {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.ie {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 0 1px 0 !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar {
|
||||
line-height: 8px !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar a {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
|
||||
background: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .number {
|
||||
color: #bbbbbb !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .keyword {
|
||||
color: #006699 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .script {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt1 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt2 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.number {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter {
|
||||
color: #afafaf !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line {
|
||||
border-right: 3px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line.highlighted {
|
||||
background-color: #6ce26c !important;
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
color: blue !important;
|
||||
background: white !important;
|
||||
border: 1px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a:hover {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
color: white !important;
|
||||
background: #6ce26c !important;
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter .string, .syntaxhighlighter .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter .keyword {
|
||||
color: #006699 !important;
|
||||
}
|
||||
.syntaxhighlighter .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter .script {
|
||||
font-weight: bold !important;
|
||||
color: #006699 !important;
|
||||
background-color: none !important;
|
||||
}
|
||||
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword {
|
||||
font-weight: bold !important;
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt1 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt2 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.number {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter {
|
||||
color: #afafaf !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line {
|
||||
border-right: 3px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line.highlighted {
|
||||
background-color: #6ce26c !important;
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
color: blue !important;
|
||||
background: white !important;
|
||||
border: 1px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a:hover {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
color: white !important;
|
||||
background: #6ce26c !important;
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter .string, .syntaxhighlighter .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter .keyword {
|
||||
color: #006699 !important;
|
||||
}
|
||||
.syntaxhighlighter .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter .script {
|
||||
font-weight: bold !important;
|
||||
color: #006699 !important;
|
||||
background-color: none !important;
|
||||
}
|
||||
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword {
|
||||
font-weight: bold !important;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.resource.ValueSet;
|
||||
|
||||
public class FhirContextTest {
|
||||
|
||||
@Test
|
||||
public void testIncrementalScan() {
|
||||
|
||||
FhirContext ctx = new FhirContext();
|
||||
ctx.getResourceDefinition(ValueSet.class);
|
||||
ctx.getResourceDefinition(Patient.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -88,6 +88,7 @@ public class JsonParserTest {
|
|||
|
||||
@Test
|
||||
public void testParseEmptyNarrative() throws ConfigurationException, DataFormatException, IOException {
|
||||
//@formatter:off
|
||||
String text = "{\n" +
|
||||
" \"resourceType\" : \"Patient\",\n" +
|
||||
" \"extension\" : [\n" +
|
||||
|
@ -100,11 +101,13 @@ public class JsonParserTest {
|
|||
" \"div\" : \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\"\n" +
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
//@formatter:on
|
||||
IResource res = ourCtx.newJsonParser().parseResource(text);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testNestedContainedResources() {
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.model.dstu.resource.Encounter;
|
|||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -72,12 +73,15 @@ public class GenericClientTest {
|
|||
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), 201, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "/Patient/44/_history/22")});
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
client.create(p1);
|
||||
MethodOutcome outcome = client.create(p1);
|
||||
assertEquals("44",outcome.getId().getIdPart());
|
||||
assertEquals("22",outcome.getId().getVersionIdPart());
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getValue().getURI().toString());
|
||||
assertEquals("POST", capt.getValue().getMethod());
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipHandler;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class CompressionTest {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompressionTest.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static FhirContext ourCtx;
|
||||
|
||||
public static String decompress(byte[] theResource) {
|
||||
GZIPInputStream is;
|
||||
try {
|
||||
is = new GZIPInputStream(new ByteArrayInputStream(theResource));
|
||||
return IOUtils.toString(is, "UTF-8");
|
||||
} catch (IOException e) {
|
||||
throw new DataFormatException("Failed to decompress contents", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
{
|
||||
/*
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||
httpGet.addHeader(Constants.HEADER_ACCEPT_ENCODING, "gzip");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
Header ce = status.getFirstHeader(Constants.HEADER_CONTENT_ENCODING);
|
||||
*/
|
||||
|
||||
URLConnection c = new URL("http://localhost:" + ourPort + "/Patient/1").openConnection();
|
||||
c.addRequestProperty(Constants.HEADER_ACCEPT_ENCODING, "gzip");
|
||||
|
||||
String enc = c.getContentEncoding();
|
||||
|
||||
assertEquals("gzip", enc);
|
||||
|
||||
byte[] responseContentBytes = IOUtils.toByteArray(c.getInputStream());
|
||||
String responseContent = decompress(responseContentBytes);
|
||||
|
||||
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class,responseContent).getIdentifierFirstRep();
|
||||
assertEquals("1", dt.getSystem().getValueAsString());
|
||||
assertEquals(null, dt.getValue().getValueAsString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVRead() throws Exception {
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/2");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
IdentifierDt dt = ourCtx.newXmlParser().parseResource(Patient.class,responseContent).getIdentifierFirstRep();
|
||||
assertEquals("1", dt.getSystem().getValueAsString());
|
||||
assertEquals("2", dt.getValue().getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyProvider patientProvider = new DummyProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
ourCtx = servlet.getFhirContext();
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
// GzipHandler gh = new GzipHandler();
|
||||
// gh.setHandler(proxyHandler);
|
||||
// gh.setMimeTypes(Constants.CT_ATOM_XML+','+Constants.CT_FHIR_JSON+','+Constants.CT_FHIR_XML);
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyProvider implements IResourceProvider {
|
||||
|
||||
@Read(version = true)
|
||||
public Patient findPatient(@IdParam IdDt theId) {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier(theId.getIdPart(), theId.getVersionIdPart());
|
||||
patient.setId("Patient/1/_history/1");
|
||||
return patient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -52,7 +52,7 @@ public class PagingTest {
|
|||
String link;
|
||||
String base = "http://localhost:" + ourPort;
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(base+ "/Patient?");
|
||||
HttpGet httpGet = new HttpGet(base+ "/Patient?_format=xml&_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
@ -60,7 +60,7 @@ public class PagingTest {
|
|||
assertEquals(5, bundle.getEntries().size());
|
||||
assertEquals("0", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("4", bundle.getEntries().get(4).getId().getIdPart());
|
||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=5&" + Constants.PARAM_COUNT + "=5", bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=5&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkNext().getValue());
|
||||
assertNull(bundle.getLinkPrevious().getValue());
|
||||
link=bundle.getLinkNext().getValue();
|
||||
}
|
||||
|
@ -74,7 +74,30 @@ public class PagingTest {
|
|||
assertEquals("5", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("9", bundle.getEntries().get(4).getId().getIdPart());
|
||||
assertNull(bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=5", bundle.getLinkPrevious().getValue());
|
||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchInexactOffset() throws Exception {
|
||||
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
||||
when(myPagingProvider.getMaximumPageSize()).thenReturn(9);
|
||||
when(myPagingProvider.storeResultList(any(IBundleProvider.class))).thenReturn("ABCD");
|
||||
when(myPagingProvider.retrieveResultList(eq("ABCD"))).thenReturn(ourBundleProvider);
|
||||
|
||||
String base = "http://localhost:" + ourPort;
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=8&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("8", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("9", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertNull(bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=3&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,263 +0,0 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
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.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
public class TesterTest {
|
||||
|
||||
private int myPort;
|
||||
private Server myServer;
|
||||
private FhirContext myCtx;
|
||||
private RestfulServer myRestfulServer;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
myPort = RandomServerPortProvider.findFreePort();
|
||||
myPort = 8888;
|
||||
myServer = new Server(myPort);
|
||||
myCtx = new FhirContext();
|
||||
myCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
myRestfulServer = new RestfulServer(myCtx);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
|
||||
testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context");
|
||||
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
|
||||
ServletHolder handler = new ServletHolder();
|
||||
handler.setServlet(testerServlet);
|
||||
proxyHandler.addServlet(handler, "/fhir/tester/*");
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(myRestfulServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
myServer.setHandler(proxyHandler);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
myServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTester() throws Exception {
|
||||
if (true) return;
|
||||
|
||||
myRestfulServer.setProviders(new SearchProvider(), new GlobalHistoryProvider());
|
||||
myServer.start();
|
||||
|
||||
Thread.sleep(9999999L);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class SearchProvider {
|
||||
|
||||
private int myNextId = 1;
|
||||
private HashMap<String, Patient> myIdToPatient;
|
||||
|
||||
private static Patient createPatient() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00001");
|
||||
patient.addName();
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.getGender().setText("M");
|
||||
return patient;
|
||||
}
|
||||
|
||||
public SearchProvider() {
|
||||
myIdToPatient = new HashMap<String, Patient>();
|
||||
{
|
||||
Patient patient = createPatient();
|
||||
myIdToPatient.put("" + myNextId++, patient);
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.getIdentifier().add(new IdentifierDt());
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00002");
|
||||
patient.getName().add(new HumanNameDt());
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientTwo");
|
||||
patient.getGender().setText("F");
|
||||
myIdToPatient.put("" + myNextId++, patient);
|
||||
}
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||
IdDt id = new IdDt(myNextId++);
|
||||
myIdToPatient.put(id.getValueAsString(), thePatient);
|
||||
return new MethodOutcome(id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Validate
|
||||
public MethodOutcome validatePatient(@ResourceParam Patient thePatient) {
|
||||
MethodOutcome outcome = new MethodOutcome();
|
||||
outcome.setOperationOutcome(new OperationOutcome());
|
||||
outcome.getOperationOutcome().addIssue().setDetails("This is a detected issue");
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
||||
@Update
|
||||
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
||||
myIdToPatient.put(theId.getValueAsString(), thePatient);
|
||||
return new MethodOutcome(theId);
|
||||
}
|
||||
|
||||
@Delete(type=Patient.class)
|
||||
public MethodOutcome deletePatient(@IdParam IdDt theId) {
|
||||
myIdToPatient.remove(theId.getValue());
|
||||
return new MethodOutcome();
|
||||
}
|
||||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number). Example system 'urn:hapitest:mrns', example MRN '00002'") @RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
|
||||
for (Patient next : myIdToPatient.values()) {
|
||||
for (IdentifierDt nextId : next.getIdentifier()) {
|
||||
if (nextId.matchesSystemAndValue(theIdentifier)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the resource by its identifier
|
||||
*
|
||||
* @param theId
|
||||
* The resource identity
|
||||
* @return The resource
|
||||
*/
|
||||
@Read(type = Patient.class)
|
||||
public Patient getPatientById(@IdParam IdDt theId) {
|
||||
return myIdToPatient.get(theId.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the resource by its identifier
|
||||
*
|
||||
* @param theId
|
||||
* The resource identity
|
||||
* @return The resource
|
||||
*/
|
||||
@History(type = Patient.class)
|
||||
public List<Patient> getPatientHistory(@IdParam IdDt theId) {
|
||||
Patient patient = myIdToPatient.get(theId.getValue());
|
||||
if (patient==null) {
|
||||
throw new ResourceNotFoundException(Patient.class, theId);
|
||||
}
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
for (int i = 0; i < 5;i++) {
|
||||
Patient pat = createPatient();
|
||||
pat.setId(theId);
|
||||
pat.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, ""+i);
|
||||
retVal.add(pat);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class GlobalHistoryProvider {
|
||||
|
||||
private InstantDt myLastSince;
|
||||
private IntegerDt myLastCount;
|
||||
|
||||
@History
|
||||
public List<IResource> getGlobalHistory(@Since InstantDt theSince, @Count IntegerDt theCount) {
|
||||
myLastSince = theSince;
|
||||
myLastCount = theCount;
|
||||
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
||||
|
||||
IResource p = SearchProvider.createPatient();
|
||||
p.setId(new IdDt("1"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt("A"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new InstantDt("2012-01-01T00:00:01"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt("2012-01-01T01:00:01"));
|
||||
retVal.add(p);
|
||||
|
||||
p = SearchProvider.createPatient();
|
||||
p.setId(new IdDt("1"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt("B"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new InstantDt("2012-01-01T00:00:01"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt("2012-01-01T01:00:03"));
|
||||
retVal.add(p);
|
||||
|
||||
p = createOrganization();
|
||||
p.setId(new IdDt("1"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt("A"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new InstantDt("2013-01-01T00:00:01"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt("2013-01-01T01:00:01"));
|
||||
retVal.add(p);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static Organization createOrganization() {
|
||||
Organization retVal = new Organization();
|
||||
retVal.addIdentifier();
|
||||
retVal.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
retVal.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
retVal.getIdentifier().get(0).setValue("00001");
|
||||
retVal.getName().setValue("Test Org");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,779 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2011-2013, HL7, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of HL7 nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Generated on Mon, Feb 3, 2014 23:47+1100 for FHIR v0.80
|
||||
-->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://hl7.org/fhir" xmlns:xhtml="http://www.w3.org/1999/xhtml" targetNamespace="http://hl7.org/fhir" elementFormDefault="qualified" version="0.80">
|
||||
<xs:include schemaLocation="fhir-base.xsd"/>
|
||||
<xs:element name="Conformance" type="Conformance">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:complexType name="Conformance">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Resource">
|
||||
<xs:sequence>
|
||||
<xs:element name="identifier" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The identifier that is used to identify this conformance statement when it is referenced in a specification, model, design or an instance (should be globally unique OID, UUID, or URI).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="version" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The identifier that is used to identify this version of the conformance statement when it is referenced in a specification, model, design or instance. This is an arbitrary value managed by the profile author manually and the value should be a timestamp.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="name" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A free text natural language name identifying the conformance statement.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="publisher" minOccurs="1" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name of Organization publishing this conformance statement.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="telecom" minOccurs="0" maxOccurs="unbounded" type="Contact">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Contacts for Organization relevant to this conformance statement. The contacts may be a website, email, phone numbers, etc.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A free text natural language description of the conformance statement and its use. Typically, this is used when the profile describes a desired rather than an actual solution, for example as a formal expression of requirements as part of an RFP.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="status" minOccurs="0" maxOccurs="1" type="ConformanceStatementStatus">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The status of this conformance statement.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="experimental" minOccurs="0" maxOccurs="1" type="boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A flag to indicate that this conformance statement is authored for testing purposes (or education/evaluation/marketing), and is not intended to be used for genuine usage.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="date" minOccurs="1" maxOccurs="1" type="dateTime">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The date when the conformance statement was published.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="software" type="Conformance.Software" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Software that is covered by this conformance statement. It is used when the profile describes the capabilities of a particular software version, independent of an installation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="implementation" type="Conformance.Implementation" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifies a specific implementation instance that is described by the conformance statement - i.e. a particular installation, rather than the capabilities of a software program.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="fhirVersion" minOccurs="1" maxOccurs="1" type="id">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The version of the FHIR specification on which this conformance statement is based.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="acceptUnknown" minOccurs="1" maxOccurs="1" type="boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A flag that indicates whether the application accepts unknown elements as part of a resource.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="format" minOccurs="1" maxOccurs="unbounded" type="code">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of the formats supported by this implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="profile" minOccurs="0" maxOccurs="unbounded" type="ResourceReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of profiles supported by the system. For a server, "supported by the system" means the system hosts/produces a set of recourses, conformant to a particular profile, and allows its clients to search using this profile and to find appropriate data. For a client, it means the system will search by this profile and process data according to the guidance implicit in the profile.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="rest" type="Conformance.Rest" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A definition of the restful capabilities of the solution, if any.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="messaging" type="Conformance.Messaging" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A description of the messaging capabilities of the solution.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="document" type="Conformance.Document" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A document definition.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Software">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="name" minOccurs="1" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Name software is known by.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="version" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The version identifier for the software covered by this statement.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="releaseDate" minOccurs="0" maxOccurs="1" type="dateTime">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Date this version of the software released.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Implementation">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="description" minOccurs="1" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Information about the specific installation that this conformance statement relates to.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="url" minOccurs="0" maxOccurs="1" type="uri">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A base URL for the implementation. This forms the base for REST interfaces as well as the mailbox and document interfaces.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Rest">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="mode" minOccurs="1" maxOccurs="1" type="RestfulConformanceMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifies whether this portion of the statement is describing ability to initiate or receive restful operations.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Information about the system's restful capabilities that apply across all applications, such as security.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="security" type="Conformance.Security" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Information about security of implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="resource" type="Conformance.Resource" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A specification of the restful capabilities of the solution for a specific resource type.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="operation" type="Conformance.Operation1" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A specification of restful operations supported by the system.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="query" type="Conformance.Query" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Definition of a named query and its parameters and their meaning.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentMailbox" minOccurs="0" maxOccurs="unbounded" type="uri">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of profiles that this server implements for accepting documents in the mailbox. If this list is empty, then documents are not accepted. The base specification has the profile identifier "http://hl7.org/fhir/documents/mailbox". Other specifications can declare their own identifier for this purpose.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Security">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="cors" minOccurs="0" maxOccurs="1" type="boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Server adds CORS headers when responding to requests - this enables javascript applications to yuse the server.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="service" minOccurs="0" maxOccurs="unbounded" type="CodeableConcept">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Types of security services are supported/required by the system.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="description" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>General description of how security works.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="certificate" type="Conformance.Certificate" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Certificates associated with security profiles.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Certificate">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="type" minOccurs="0" maxOccurs="1" type="code">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Mime type for certificate.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="blob" minOccurs="0" maxOccurs="1" type="base64Binary">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Actual certificate.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Resource">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="type" minOccurs="1" maxOccurs="1" type="code">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A type of resource exposed via the restful interface.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="profile" minOccurs="0" maxOccurs="1" type="ResourceReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A specification of the profile that describes the solution's support for the resource, including any constraints on cardinality, bindings, lengths or other limitations.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="operation" type="Conformance.Operation" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifies a restful operation supported by the solution.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="readHistory" minOccurs="0" maxOccurs="1" type="boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A flag for whether the server is able to return past versions as part of the vRead operation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="updateCreate" minOccurs="0" maxOccurs="1" type="boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A flag to indicate that the server allows the client to create new identities on the server. If the update operation is used (client) or allowed (server) to a new location where a resource doesn't already exist. This means that the server allows the client to create new identities on the server.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="searchInclude" minOccurs="0" maxOccurs="unbounded" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of _include values supported by the server.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="searchParam" type="Conformance.SearchParam" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Additional search parameters for implementations to support and/or make use of.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Operation">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="code" minOccurs="1" maxOccurs="1" type="RestfulOperationType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Coded identifier of the operation, supported by the system resource.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Guidance specific to the implementation of this operation, such as 'delete is a logical delete' or 'updates are only allowed with version id' or 'creates permitted from pre-authorized certificates only'.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.SearchParam">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="name" minOccurs="1" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the search parameter used in the interface.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="definition" minOccurs="0" maxOccurs="1" type="uri">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A formal reference to where this parameter was first defined, so that a client can be confident of the meaning of the search parameter.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="type" minOccurs="1" maxOccurs="1" type="SearchParamType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The type of value a search parameter refers to, and how the content is interpreted.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This allows documentation of any distinct behaviors about how the search parameter is used. For example, text matching algorithms.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="target" minOccurs="0" maxOccurs="unbounded" type="code">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Types of resource (if a resource is referenced).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="chain" minOccurs="0" maxOccurs="unbounded" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Chained names supported.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Operation1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="code" minOccurs="1" maxOccurs="1" type="RestfulOperationSystem">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A coded identifier of the operation, supported by the system.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Guidance specific to the implementation of this operation, such as limitations on the kind of transactions allowed, or information about system wide search is implemented.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Query">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="name" minOccurs="1" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of a query, which is used in the _query parameter when the query is called.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="definition" minOccurs="1" maxOccurs="1" type="uri">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifies the custom query, defined either in FHIR core or another profile.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Additional information about how the query functions in this particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="parameter" type="Conformance.SearchParam" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Identifies which of the parameters for the named query are supported.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Messaging">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="endpoint" minOccurs="0" maxOccurs="1" type="uri">
|
||||
<xs:annotation>
|
||||
<xs:documentation>An address to which messages and/or replies are to be sent.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="reliableCache" minOccurs="0" maxOccurs="1" type="integer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Length if the receiver's reliable messaging cache (if a receiver) or how long the cache length on the receiver should be (if a sender).</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Documentation about the system's messaging capabilities for this endpoint not otherwise documented by the conformance statement. For example, process for becoming an authorized messaging exchange partner.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="event" type="Conformance.Event" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A description of the solution's support for an event at this end point.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Event">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="code" minOccurs="1" maxOccurs="1" type="Coding">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A coded identifier of a supported messaging event.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="category" minOccurs="0" maxOccurs="1" type="MessageSignificanceCategory">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The impact of the content of the message.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="mode" minOccurs="1" maxOccurs="1" type="ConformanceEventMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The mode of this event declaration - whether application is sender or receiver.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="protocol" minOccurs="0" maxOccurs="unbounded" type="Coding">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of the messaging transport protocol(s) identifiers, supported by this endpoint.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="focus" minOccurs="1" maxOccurs="1" type="code">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A resource associated with the event. This is the resource that defines the event.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="request" minOccurs="1" maxOccurs="1" type="ResourceReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Information about the request for this event.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="response" minOccurs="1" maxOccurs="1" type="ResourceReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Information about the response for this event.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Guidance on how this event is handled, such as internal system trigger points, business rules, etc.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="Conformance.Document">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A conformance statement is a set of requirements for a desired implementation or a description of how a target application fulfills those requirements in a particular implementation.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="BackboneElement">
|
||||
<xs:sequence>
|
||||
<xs:element name="mode" minOccurs="1" maxOccurs="1" type="DocumentMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Mode of this document declaration - whether application is producer or consumer.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="documentation" minOccurs="0" maxOccurs="1" type="string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A description of how the application supports or uses the specified document profile. For example, when are documents created, what action is taken with consumed documents, etc.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="profile" minOccurs="1" maxOccurs="1" type="ResourceReference">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A constraint on a resource used in the document.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="DocumentMode-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="producer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application produces documents of the specified type.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="consumer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application consumes documents of the specified type.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="DocumentMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Whether the application produces or consumes documents</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="DocumentMode-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="RestfulConformanceMode-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="client">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application acts as a server for this resource.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="server">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application acts as a client for this resource.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="RestfulConformanceMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The mode of a RESTful conformance statement</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="RestfulConformanceMode-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="ConformanceEventMode-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="sender">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application sends requests and receives responses.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="receiver">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The application receives requests and sends responses.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="ConformanceEventMode">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The mode of a message conformance statement</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="ConformanceEventMode-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="MessageSignificanceCategory-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Consequence">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The message represents/requests a change that should not be processed more than once. E.g. Making a booking for an appointment.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Currency">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The message represents a response to query for current information. Retrospective processing is wrong and/or wasteful.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Notification">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The content is not necessarily intended to be current, and it can be reprocessed, though there may be version issues created by processing old notifications.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="MessageSignificanceCategory">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The impact of the content of a message</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="MessageSignificanceCategory-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="RestfulOperationType-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="read">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="vread">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="update">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="delete">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="history-instance">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="validate">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="history-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="create">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="search-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="RestfulOperationType">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Operations supported by REST at the type or instance level</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="RestfulOperationType-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="ConformanceStatementStatus-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="draft">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This conformance statement is still under development.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="active">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This conformance statement is ready for use in production systems.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="retired">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This conformance statement has been withdrawn or superceded and should no longer be used.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="ConformanceStatementStatus">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The status of this conformance statement</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="ConformanceStatementStatus-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="RestfulOperationSystem-list">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="transaction">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="search-system">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="history-system">
|
||||
<xs:annotation>
|
||||
<xs:documentation></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="RestfulOperationSystem">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Operations supported by REST at the system level</xs:documentation>
|
||||
<xs:documentation>If the element is present, it must have either a @value, an @id, or extensions</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexContent>
|
||||
<xs:extension base="Element">
|
||||
<xs:attribute name="value" type="RestfulOperationSystem-list" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<?xml version="1.0" encoding="UTF-8"?><Patient xmlns="http://hl7.org/fhir">
|
||||
<text>
|
||||
<status value="generated"/>
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<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="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
<project>hapi-fhir-base</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
|
@ -16,9 +21,17 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<wb-module deploy-name="hapi-fhir-jpaserver-base">
|
||||
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
||||
</wb-module>
|
||||
</project-modules>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<installed facet="java" version="1.6"/>
|
||||
<installed facet="jst.utility" version="1.0"/>
|
||||
</faceted-project>
|
|
@ -8,7 +8,6 @@
|
|||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-base</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
|
@ -24,7 +23,7 @@
|
|||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>${logback-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -37,13 +36,7 @@
|
|||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>${hamcrest_version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
<artifactId>commons-dbcp</artifactId>
|
||||
<version>1.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Database -->
|
||||
|
@ -51,7 +44,10 @@
|
|||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
||||
|
||||
<!-- Spring -->
|
||||
|
@ -156,18 +152,6 @@
|
|||
|
||||
|
||||
<properties>
|
||||
<junit_version>4.11</junit_version>
|
||||
<derby_version>10.10.2.0</derby_version>
|
||||
<guava_version>14.0.1</guava_version>
|
||||
<hamcrest_version>1.3</hamcrest_version>
|
||||
<!-- <hibernate_version>4.3.5.Final</hibernate_version>-->
|
||||
<hibernate_version>4.2.12.Final</hibernate_version>
|
||||
<hibernate_validator_version>5.1.0.Final</hibernate_validator_version>
|
||||
<jetty_version>9.1.1.v20140108</jetty_version>
|
||||
<mockito_version>1.9.5</mockito_version>
|
||||
<slf4j_version>1.7.2</slf4j_version>
|
||||
<spring_version>4.0.1.RELEASE</spring_version>
|
||||
|
||||
<skip-hib4>false</skip-hib4>
|
||||
</properties>
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import static org.apache.commons.lang3.StringUtils.*;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -28,7 +27,6 @@ import javax.persistence.criteria.Predicate;
|
|||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -99,7 +97,8 @@ public abstract class BaseFhirDao {
|
|||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
// cq.where(builder.equal(from.get("myResourceType"), getContext().getResourceDefinition(myResourceType).getName()));
|
||||
// cq.where(builder.equal(from.get("myResourceType"),
|
||||
// getContext().getResourceDefinition(myResourceType).getName()));
|
||||
// if (theIncludePids != null) {
|
||||
cq.where(from.get("myId").in(pids));
|
||||
// }
|
||||
|
@ -423,8 +422,7 @@ public abstract class BaseFhirDao {
|
|||
|
||||
if (nextObject instanceof QuantityDt) {
|
||||
QuantityDt nextValue = (QuantityDt) nextObject;
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(),
|
||||
nextValue.getUnits().getValue());
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
|
@ -507,8 +505,7 @@ public abstract class BaseFhirDao {
|
|||
} else if (nextObject instanceof ContactDt) {
|
||||
ContactDt nextContact = (ContactDt) nextObject;
|
||||
if (nextContact.getValue().isEmpty() == false) {
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact
|
||||
.getValue().getValueAsString());
|
||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
}
|
||||
|
@ -676,7 +673,8 @@ public abstract class BaseFhirDao {
|
|||
}
|
||||
|
||||
InstantDt createHistoryToTimestamp() {
|
||||
// final InstantDt end = new InstantDt(DateUtils.addSeconds(DateUtils.truncate(new Date(), Calendar.SECOND), -1));
|
||||
// final InstantDt end = new InstantDt(DateUtils.addSeconds(DateUtils.truncate(new Date(), Calendar.SECOND),
|
||||
// -1));
|
||||
return InstantDt.withCurrentTime();
|
||||
}
|
||||
|
||||
|
@ -717,6 +715,15 @@ public abstract class BaseFhirDao {
|
|||
|
||||
theEntity.setResourceType(toResourceName(theResource));
|
||||
|
||||
List<ResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, ResourceReferenceDt.class);
|
||||
for (ResourceReferenceDt nextRef : refs) {
|
||||
if (nextRef.getReference().isEmpty()==false) {
|
||||
if (nextRef.getReference().hasVersionIdPart()) {
|
||||
nextRef.setReference(nextRef.getReference().toUnqualifiedVersionless());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String encoded = myConfig.getResourceEncoding().newParser(myContext).encodeResourceToString(theResource);
|
||||
ResourceEncodingEnum encoding = myConfig.getResourceEncoding();
|
||||
theEntity.setEncoding(encoding);
|
||||
|
@ -776,6 +783,11 @@ public abstract class BaseFhirDao {
|
|||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, theEntity.getVersion());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
|
||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
|
||||
|
||||
if (theEntity.getDeleted()!=null) {
|
||||
ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted()));
|
||||
}
|
||||
|
||||
Collection<? extends BaseTag> tags = theEntity.getTags();
|
||||
if (tags.size() > 0) {
|
||||
TagList tagList = new TagList();
|
||||
|
@ -795,7 +807,7 @@ public abstract class BaseFhirDao {
|
|||
return myContext.getResourceDefinition(theResourceType).getName();
|
||||
}
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, boolean theDelete) {
|
||||
if (entity.getPublished() == null) {
|
||||
entity.setPublished(new Date());
|
||||
}
|
||||
|
@ -813,12 +825,28 @@ public abstract class BaseFhirDao {
|
|||
Collection<ResourceIndexedSearchParamDate> paramsDate = new ArrayList<ResourceIndexedSearchParamDate>(entity.getParamsDate());
|
||||
Collection<ResourceLink> resourceLinks = new ArrayList<ResourceLink>(entity.getResourceLinks());
|
||||
|
||||
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
|
||||
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
|
||||
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
|
||||
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
|
||||
final List<ResourceLink> links = extractResourceLinks(entity, theResource);
|
||||
final List<ResourceIndexedSearchParamString> stringParams;
|
||||
final List<ResourceIndexedSearchParamToken> tokenParams;
|
||||
final List<ResourceIndexedSearchParamNumber> numberParams;
|
||||
final List<ResourceIndexedSearchParamDate> dateParams;
|
||||
final List<ResourceLink> links;
|
||||
if (theDelete) {
|
||||
|
||||
stringParams = Collections.emptyList();
|
||||
tokenParams = Collections.emptyList();
|
||||
numberParams = Collections.emptyList();
|
||||
dateParams = Collections.emptyList();
|
||||
links = Collections.emptyList();
|
||||
entity.setDeleted(new Date());
|
||||
entity.setUpdated(new Date());
|
||||
|
||||
} else {
|
||||
|
||||
stringParams = extractSearchParamStrings(entity, theResource);
|
||||
tokenParams = extractSearchParamTokens(entity, theResource);
|
||||
numberParams = extractSearchParamNumber(entity, theResource);
|
||||
dateParams = extractSearchParamDates(entity, theResource);
|
||||
links = extractResourceLinks(entity, theResource);
|
||||
populateResourceIntoEntity(theResource, entity);
|
||||
|
||||
entity.setUpdated(new Date());
|
||||
|
@ -833,6 +861,8 @@ public abstract class BaseFhirDao {
|
|||
entity.setResourceLinks(links);
|
||||
entity.setHasLinks(links.isEmpty() == false);
|
||||
|
||||
}
|
||||
|
||||
if (entity.getId() == null) {
|
||||
myEntityManager.persist(entity);
|
||||
} else {
|
||||
|
@ -885,7 +915,10 @@ public abstract class BaseFhirDao {
|
|||
}
|
||||
|
||||
myEntityManager.flush();
|
||||
|
||||
if (theResource!=null) {
|
||||
theResource.setId(new IdDt(entity.getResourceType(), entity.getId().toString(), Long.toString(entity.getVersion())));
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
|
@ -479,7 +480,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
ResourceTable entity = new ResourceTable();
|
||||
entity.setResourceType(toResourceName(theResource));
|
||||
|
||||
updateEntity(theResource, entity, false);
|
||||
updateEntity(theResource, entity, false, false);
|
||||
|
||||
MethodOutcome outcome = toMethodOutcome(entity);
|
||||
return outcome;
|
||||
|
@ -617,6 +618,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
BaseHasResource entity = readEntity(theId);
|
||||
|
||||
T retVal = toResource(myResourceType, entity);
|
||||
|
||||
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
|
||||
if (deleted != null && !deleted.isEmpty()) {
|
||||
throw new ResourceGoneException("Resource was deleted at " + deleted.getValueAsString());
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -764,8 +771,6 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IResource> theResourceListToPopulate) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
|
@ -952,7 +957,23 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
// });
|
||||
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
ResourceTable savedEntity = updateEntity(theResource, entity, true);
|
||||
if (theId.hasVersionIdPart() && theId.getVersionIdPartAsLong().longValue() != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
ResourceTable savedEntity = updateEntity(theResource, entity, true, false);
|
||||
|
||||
return toMethodOutcome(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome delete(IdDt theId) {
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart() && theId.getVersionIdPartAsLong().longValue() != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, true);
|
||||
|
||||
return toMethodOutcome(savedEntity);
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
|
|||
for (int i = 0; i < theResources.size(); i++) {
|
||||
IResource resource = theResources.get(i);
|
||||
ResourceTable table = persistedResources.get(i);
|
||||
updateEntity(resource, table, table.getId() != null);
|
||||
updateEntity(resource, table, table.getId() != null, false);
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
|
|
@ -20,6 +20,8 @@ public interface IFhirResourceDao<T extends IResource> {
|
|||
|
||||
MethodOutcome create(T theResource);
|
||||
|
||||
MethodOutcome delete(IdDt theResource);
|
||||
|
||||
TagList getAllResourceTags();
|
||||
|
||||
Class<T> getResourceType();
|
||||
|
|
|
@ -17,6 +17,10 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
@MappedSuperclass
|
||||
public abstract class BaseHasResource {
|
||||
|
||||
@Column(name = "RES_DELETED_AT", nullable = true)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date myDeleted;
|
||||
|
||||
@Column(name = "RES_ENCODING", nullable = false, length = 5)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ResourceEncodingEnum myEncoding;
|
||||
|
@ -33,14 +37,16 @@ public abstract class BaseHasResource {
|
|||
@Column(name = "RES_UPDATED", nullable = false)
|
||||
private Date myUpdated;
|
||||
|
||||
public abstract BaseTag addTag(TagDefinition theDef);
|
||||
|
||||
public Date getDeleted() {
|
||||
return myDeleted;
|
||||
}
|
||||
|
||||
public ResourceEncodingEnum getEncoding() {
|
||||
return myEncoding;
|
||||
}
|
||||
|
||||
public abstract String getResourceType();
|
||||
|
||||
public abstract Collection<? extends BaseTag> getTags();
|
||||
|
||||
public abstract IdDt getIdDt();
|
||||
|
||||
public InstantDt getPublished() {
|
||||
|
@ -51,12 +57,20 @@ public abstract class BaseHasResource {
|
|||
return myResource;
|
||||
}
|
||||
|
||||
public abstract String getResourceType();
|
||||
|
||||
public abstract Collection<? extends BaseTag> getTags();
|
||||
|
||||
public InstantDt getUpdated() {
|
||||
return new InstantDt(myUpdated);
|
||||
}
|
||||
|
||||
public abstract long getVersion();
|
||||
|
||||
public void setDeleted(Date theDate) {
|
||||
myDeleted = theDate;
|
||||
}
|
||||
|
||||
public void setEncoding(ResourceEncodingEnum theEncoding) {
|
||||
myEncoding = theEncoding;
|
||||
}
|
||||
|
@ -81,6 +95,4 @@ public abstract class BaseHasResource {
|
|||
myUpdated = theUpdated.getValue();
|
||||
}
|
||||
|
||||
public abstract BaseTag addTag(TagDefinition theDef);
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
|
|||
|
||||
@GeneratedValue(strategy=GenerationType.AUTO)
|
||||
@Id
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
@ManyToOne()
|
||||
|
|
|
@ -28,10 +28,10 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
@org.hibernate.annotations.Table(appliesTo = "HFJ_RESOURCE", indexes = { @Index(name = "IDX_RES_DATE", columnNames = { "RES_UPDATED" }) })
|
||||
public class ResourceTable extends BaseHasResource implements Serializable {
|
||||
static final int RESTYPE_LEN = 30;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
static final int RESTYPE_LEN = 30;
|
||||
|
||||
@Column(name = "SP_HAS_LINKS")
|
||||
private boolean myHasLinks;
|
||||
|
||||
|
@ -256,6 +256,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
retVal.setUpdated(getUpdated());
|
||||
retVal.setEncoding(getEncoding());
|
||||
retVal.setResource(getResource());
|
||||
retVal.setDeleted(getDeleted());
|
||||
|
||||
for (ResourceTag next : getTags()) {
|
||||
retVal.addTag(next);
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
|
@ -19,13 +21,16 @@ import ca.uhn.fhir.rest.annotation.Read;
|
|||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
public class JpaResourceProvider<T extends IResource> implements IResourceProvider {
|
||||
|
||||
private FhirContext myContext = new FhirContext();
|
||||
@Autowired(required=true)
|
||||
private FhirContext myContext;
|
||||
|
||||
private IFhirResourceDao<T> myDao;
|
||||
|
||||
public JpaResourceProvider() {
|
||||
|
@ -41,6 +46,11 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
return myDao.create(theResource);
|
||||
}
|
||||
|
||||
@Delete
|
||||
public MethodOutcome delete(@IdParam IdDt theResource) {
|
||||
return myDao.delete(theResource);
|
||||
}
|
||||
|
||||
public FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
@ -50,13 +60,13 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@History
|
||||
public IBundleProvider getHistoryForResourceType(@Since Date theDate) {
|
||||
return myDao.history(theDate);
|
||||
public IBundleProvider getHistoryForResourceInstance(@IdParam IdDt theId, @Since Date theDate) {
|
||||
return myDao.history(theId.getIdPartAsLong(), theDate);
|
||||
}
|
||||
|
||||
@History
|
||||
public IBundleProvider getHistoryForResourceInstance(@IdParam IdDt theId, @Since Date theDate) {
|
||||
return myDao.history(theId.getIdPartAsLong(), theDate);
|
||||
public IBundleProvider getHistoryForResourceType(@Since Date theDate) {
|
||||
return myDao.history(theDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,13 +75,13 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@GetTags
|
||||
public TagList getTagsForResourceType() {
|
||||
return myDao.getAllResourceTags();
|
||||
public TagList getTagsForResourceInstance(@IdParam IdDt theResourceId) {
|
||||
return myDao.getTags(theResourceId);
|
||||
}
|
||||
|
||||
@GetTags
|
||||
public TagList getTagsForResourceInstance(@IdParam IdDt theResourceId) {
|
||||
return myDao.getTags(theResourceId);
|
||||
public TagList getTagsForResourceType() {
|
||||
return myDao.getAllResourceTags();
|
||||
}
|
||||
|
||||
@Read(version=true)
|
||||
|
@ -79,6 +89,10 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
return myDao.read(theId);
|
||||
}
|
||||
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
@Required
|
||||
public void setDao(IFhirResourceDao<T> theDao) {
|
||||
myDao = theDao;
|
||||
|
@ -89,4 +103,12 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
return myDao.update(theResource, theId);
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(new OperationOutcome());
|
||||
retVal.getOperationOutcome().addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Resource validates successfully");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
|
@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.param.ReferenceParam;
|
|||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
|
||||
public class FhirResourceDaoTest {
|
||||
|
||||
|
@ -54,10 +55,28 @@ public class FhirResourceDaoTest {
|
|||
private static IFhirResourceDao<DiagnosticReport> ourDiagnosticReportDao;
|
||||
private static IFhirResourceDao<Organization> ourOrganizationDao;
|
||||
private static IFhirResourceDao<Location> ourLocationDao;
|
||||
|
||||
private static Date ourTestStarted;
|
||||
|
||||
private static IFhirResourceDao<Encounter> ourEncounterDao;
|
||||
|
||||
@Test
|
||||
public void testStoreUnversionedResources() {
|
||||
Organization o1 = new Organization();
|
||||
o1.getName().setValue("AAA");
|
||||
IdDt o1id = ourOrganizationDao.create(o1).getId();
|
||||
assertTrue(o1id.hasVersionIdPart());
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addName().addFamily("AAAA");
|
||||
p1.getManagingOrganization().setReference(o1id);
|
||||
IdDt p1id = ourPatientDao.create(p1).getId();
|
||||
|
||||
p1 = ourPatientDao.read(p1id);
|
||||
|
||||
assertFalse(p1.getManagingOrganization().getReference().hasVersionIdPart());
|
||||
assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdParam() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -311,7 +330,6 @@ public class FhirResourceDaoTest {
|
|||
assertEquals(1, patients.size());
|
||||
assertEquals(id1.getIdPart(), patients.get(0).getId().getIdPart());
|
||||
|
||||
|
||||
params = new HashMap<String, IQueryParameterType>();
|
||||
params.put(Patient.SP_FAMILY, new StringDt("testSearchNameParam01Foo"));
|
||||
patients = toList(ourPatientDao.search(params));
|
||||
|
@ -438,6 +456,69 @@ public class FhirResourceDaoTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
int initialHistory = ourPatientDao.history(null).size();
|
||||
|
||||
IdDt id1;
|
||||
IdDt id2;
|
||||
IdDt id2b;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:system", "001");
|
||||
patient.addName().addFamily("Tester_testDelete").addGiven("Joe");
|
||||
id1 = ourPatientDao.create(patient).getId();
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:system", "002");
|
||||
patient.addName().addFamily("Tester_testDelete").addGiven("John");
|
||||
id2 = ourPatientDao.create(patient).getId();
|
||||
}
|
||||
{
|
||||
Patient patient = ourPatientDao.read(id2);
|
||||
patient.addIdentifier("ZZZZZZZ", "ZZZZZZZZZ");
|
||||
id2b = ourPatientDao.update(patient, id2).getId();
|
||||
}
|
||||
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] { id1, id2, id2b });
|
||||
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put(Patient.SP_FAMILY, new StringDt("Tester_testDelete"));
|
||||
List<Patient> patients = toList(ourPatientDao.search(params));
|
||||
assertEquals(2, patients.size());
|
||||
|
||||
ourPatientDao.delete(id1);
|
||||
|
||||
patients = toList(ourPatientDao.search(params));
|
||||
assertEquals(1, patients.size());
|
||||
|
||||
ourPatientDao.read(id1);
|
||||
try {
|
||||
ourPatientDao.read(id1.toVersionless());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
IBundleProvider history = ourPatientDao.history(null);
|
||||
assertEquals(4 + initialHistory, history.size());
|
||||
List<IResource> resources = history.getResources(0, 4);
|
||||
assertNotNull(resources.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
|
||||
|
||||
try {
|
||||
ourPatientDao.delete(id2);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
ourPatientDao.delete(id2.toVersionless());
|
||||
|
||||
patients = toList(ourPatientDao.search(params));
|
||||
assertEquals(0, patients.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithIncludes() {
|
||||
{
|
||||
|
@ -474,7 +555,6 @@ public class FhirResourceDaoTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDatePeriodParamStartOnly() {
|
||||
{
|
||||
|
@ -557,7 +637,6 @@ public class FhirResourceDaoTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends IResource> List<T> toList(IBundleProvider theSearch) {
|
||||
return (List<T>) theSearch.getResources(0, theSearch.size());
|
||||
|
@ -617,7 +696,6 @@ public class FhirResourceDaoTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchStringParamWithNonNormalized() {
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
<version>2.1.2.RELEASE</version>
|
||||
<version>${thymeleaf-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
|
@ -71,8 +71,6 @@
|
|||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<junit_version>4.11</junit_version>
|
||||
<jetty_version>9.1.1.v20140108</jetty_version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
|
||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.PatientResourceProvider;
|
||||
|
@ -178,13 +177,8 @@ public class CompleteResourceProviderTest {
|
|||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
|
||||
String serverBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
testerServlet.setServerBase(serverBase);
|
||||
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
|
||||
ServletHolder handler = new ServletHolder();
|
||||
handler.setServlet(testerServlet);
|
||||
proxyHandler.addServlet(handler, "/fhir/tester/*");
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
|
|
|
@ -25,7 +25,6 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
|
|||
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
|
||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.PatientResourceProvider;
|
||||
|
@ -89,13 +88,7 @@ public class SystemTest {
|
|||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
|
||||
String serverBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
testerServlet.setServerBase(serverBase);
|
||||
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
|
||||
ServletHolder handler = new ServletHolder();
|
||||
handler.setServlet(testerServlet);
|
||||
proxyHandler.addServlet(handler, "/fhir/tester/*");
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="target/generated-resources/tinder"/>
|
||||
<classpathentry kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
|
@ -24,7 +23,7 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</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="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
@ -32,6 +31,7 @@
|
|||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
myUnitTestDB/
|
||||
target/
|
||||
fhir/
|
||||
/bin
|
||||
nohup.out
|
||||
|
|
|
@ -5,6 +5,16 @@
|
|||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
|
@ -15,9 +25,18 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/webapp"/>
|
||||
<classpathentry kind="src" path="target/m2e-wtp/web-resources"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
||||
<attributes>
|
||||
<attribute name="hide" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||
<classpathentry kind="output" path=""/>
|
||||
</classpath>
|
|
@ -1,7 +1,12 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<wb-module deploy-name="hapi-fhir-jpaserver-uhnfhirtest">
|
||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/target/generated-resources/tinder"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/target/generated-sources/tinder"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<dependent-module archiveName="hapi-fhir-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||
<dependency-type>consumes</dependency-type>
|
||||
</dependent-module>
|
||||
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||
<dependency-type>consumes</dependency-type>
|
||||
</dependent-module>
|
||||
<property name="context-root" value="hapi-fhir-jpaserver"/>
|
||||
<property name="java-output-path" value="/hapi-fhir-jpaserver-uhnfhirtest/target/classes"/>
|
||||
</wb-module>
|
||||
</project-modules>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<fixed facet="wst.jsdt.web"/>
|
||||
<installed facet="java" version="1.6"/>
|
||||
<installed facet="jst.web" version="3.0"/>
|
||||
<installed facet="wst.jsdt.web" version="1.0"/>
|
||||
</faceted-project>
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
|
@ -0,0 +1 @@
|
|||
Window
|
|
@ -0,0 +1,2 @@
|
|||
disabled=06target
|
||||
eclipse.preferences.version=1
|
|
@ -18,16 +18,66 @@
|
|||
<artifactId>hapi-fhir-jpaserver-base</artifactId>
|
||||
<version>0.4-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>0.4-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test</artifactId>
|
||||
<version>0.4-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>${spring_version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbynet</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbyclient</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
<version>2.1.2.RELEASE</version>
|
||||
<version>${thymeleaf-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>${logback-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${slf4j_version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
@ -37,10 +87,54 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST DEPS -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
<artifactId>commons-dbcp</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -136,10 +230,24 @@
|
|||
<baseResourceName>valueset</baseResourceName>
|
||||
</baseResourceNames>
|
||||
<buildDatatypes>true</buildDatatypes>
|
||||
<targetResourceDirectory>${project.build.directory}/hapi-fhir-jpaserver/WEB-INF</targetResourceDirectory>
|
||||
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders.xml</targetResourceSpringBeansFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<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>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import java.sql.DriverManager;
|
||||
|
||||
import org.apache.derby.drda.NetworkServerControl;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
public class DerbyNetworkServer implements InitializingBean, DisposableBean {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DerbyNetworkServer.class);
|
||||
private NetworkServerControl server;
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
server.shutdown();
|
||||
try {
|
||||
ourLog.info("Shutting down derby");
|
||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||
} catch (Exception e) {
|
||||
ourLog.info("Failed to create database: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
server = new NetworkServerControl();
|
||||
server.start (null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hsqldb.Server;
|
||||
import org.hsqldb.persist.HsqlProperties;
|
||||
import org.hsqldb.server.ServerAcl.AclFormatException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
public class HsqldbServer implements SmartLifecycle {
|
||||
private final Logger logger = LoggerFactory.getLogger(HsqldbServer.class);
|
||||
private HsqlProperties properties;
|
||||
private Server server;
|
||||
private boolean running = false;
|
||||
|
||||
public HsqldbServer(Properties props) {
|
||||
properties = new HsqlProperties(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
if (server != null)
|
||||
server.checkRunning(running);
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (server == null) {
|
||||
logger.info("Starting HSQL server...");
|
||||
server = new Server();
|
||||
try {
|
||||
server.setProperties(properties);
|
||||
server.start();
|
||||
running = true;
|
||||
} catch (AclFormatException afe) {
|
||||
logger.error("Error starting HSQL server.", afe);
|
||||
throw new Error(afe);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error starting HSQL server.", e);
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
logger.info("Stopping HSQL server...");
|
||||
if (server != null) {
|
||||
server.stop();
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable runnable) {
|
||||
stop();
|
||||
runnable.run();
|
||||
}
|
||||
}
|
|
@ -3,7 +3,11 @@ package ca.uhn.fhirtest;
|
|||
import java.sql.DriverManager;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.context.support.FileSystemXmlApplicationContext;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
||||
|
@ -16,21 +20,30 @@ public class TestRestfulServer extends RestfulServer {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ClassPathXmlApplicationContext myAppCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestRestfulServer.class);
|
||||
|
||||
private ApplicationContext myAppCtx;
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
|
||||
try {
|
||||
ourLog.info("Creating database");
|
||||
DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";create=true");
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to create database: {}",e);
|
||||
}
|
||||
// try {
|
||||
// ourLog.info("Creating database");
|
||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";create=true");
|
||||
// } catch (Exception e) {
|
||||
// ourLog.error("Failed to create database: {}",e);
|
||||
// }
|
||||
|
||||
myAppCtx = new ClassPathXmlApplicationContext("fhir-spring-uhnfhirtest-config.xml", "hapi-jpaserver-springbeans.xml");
|
||||
|
||||
// myAppCtx = new ClassPathXmlApplicationContext("fhir-spring-uhnfhirtest-config.xml", "hapi-jpaserver-springbeans.xml");
|
||||
|
||||
// myAppCtx = new FileSystemXmlApplicationContext(
|
||||
// "WEB-INF/hapi-fhir-server-database-config.xml",
|
||||
// "WEB-INF/hapi-fhir-server-config.xml"
|
||||
// );
|
||||
|
||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
||||
Collection<IResourceProvider> beans = myAppCtx.getBeansOfType(IResourceProvider.class).values();
|
||||
for (IResourceProvider nextResourceProvider : beans) {
|
||||
|
@ -55,14 +68,14 @@ public class TestRestfulServer extends RestfulServer {
|
|||
public void destroy() {
|
||||
super.destroy();
|
||||
|
||||
myAppCtx.close();
|
||||
|
||||
try {
|
||||
ourLog.info("Shutting down derby");
|
||||
DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||
} catch (Exception e) {
|
||||
ourLog.info("Failed to create database: {}",e.getMessage());
|
||||
}
|
||||
// myAppCtx.close();
|
||||
//
|
||||
// try {
|
||||
// ourLog.info("Shutting down derby");
|
||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||
// } catch (Exception e) {
|
||||
// ourLog.info("Failed to create database: {}",e.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import ca.uhn.fhir.rest.server.tester.RestfulTesterServlet;
|
||||
|
||||
public class TesterServlet extends RestfulTesterServlet {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public TesterServlet() {
|
||||
String baseUrl = System.getProperty("fhir.baseurl");
|
||||
|
||||
addServerBase("fhirtest", "UHN/HAPI Test Server", baseUrl);
|
||||
addServerBase("hi", "Health Intersections Ref Server", "http://fhir.healthintersections.com.au/open");
|
||||
addServerBase("furore", "Spark - Furore Ref Server", "http://spark.furore.com/fhir");
|
||||
addServerBase("blaze", "Blaze (Orion Health)", "https://his-medicomp-gateway.orionhealth.com/blaze/fhir");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<configuration scan="true" scanPeriod="30 seconds">
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root>
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue