Start adding transaction to tester

This commit is contained in:
jamesagnew 2014-07-03 08:51:42 -04:00
parent ca83de38b8
commit ab9e681ed2
18 changed files with 225 additions and 51 deletions

View File

@ -7,11 +7,13 @@ 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.Rest;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider; import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
public class JpaConformanceProvider extends ServerConformanceProvider { public class JpaConformanceProvider extends ServerConformanceProvider {
private String myImplementationDescription;
private IFhirSystemDao mySystemDao; private IFhirSystemDao mySystemDao;
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) { public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
@ -19,24 +21,29 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
mySystemDao = theSystemDao; mySystemDao = theSystemDao;
super.setCache(false); super.setCache(false);
} }
@Override @Override
public Conformance getServerConformance() { public Conformance getServerConformance() {
Map<String, Long> counts = mySystemDao.getResourceCounts(); Map<String, Long> counts = mySystemDao.getResourceCounts();
Conformance retVal = super.getServerConformance(); Conformance retVal = super.getServerConformance();
for (Rest nextRest : retVal.getRest()) { for (Rest nextRest : retVal.getRest()) {
for (RestResource nextResource : nextRest.getResource()) { for (RestResource nextResource : nextRest.getResource()) {
Long count = counts.get(nextResource.getType().getValueAsString()); Long count = counts.get(nextResource.getType().getValueAsString());
if (count!=null) { if (count != null) {
nextResource.addUndeclaredExtension(false, "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount", new DecimalDt(count)); nextResource.addUndeclaredExtension(false, "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount", new DecimalDt(count));
} }
} }
} }
retVal.getImplementation().setDescription(myImplementationDescription);
return retVal; return retVal;
} }
public void setImplementationDescription(String theImplDesc) {
myImplementationDescription = theImplDesc;
}
} }

View File

@ -12,7 +12,7 @@
<dependent-module archiveName="hapi-fhir-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base"> <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> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF"> <dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type> <dependency-type>consumes</dependency-type>
</dependent-module> </dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF"> <dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">

View File

@ -148,6 +148,31 @@
<target>1.7</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId></groupId>
<artifactId></artifactId>
<versionRange>[0.4,)</versionRange>
<goals>
<goal></goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
<plugins> <plugins>

View File

@ -55,7 +55,10 @@ public class TestRestfulServer extends RestfulServer {
JpaSystemProvider sp = new JpaSystemProvider(systemDao); JpaSystemProvider sp = new JpaSystemProvider(systemDao);
setPlainProviders(sp); setPlainProviders(sp);
String implDesc = getInitParameter("ImplementationDescription");
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao); JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
confProvider.setImplementationDescription(implDesc);
setServerConformanceProvider(confProvider); setServerConformanceProvider(confProvider);
setUseBrowserFriendlyContentTypes(true); setUseBrowserFriendlyContentTypes(true);

View File

@ -30,7 +30,7 @@
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>--> <!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
<!-- <property name="url" value="jdbc:derby:directory:#{systemProperties['fhir.db.location']};create=true" /> --> <!-- <property name="url" value="jdbc:derby:directory:#{systemProperties['fhir.db.location']};create=true" /> -->
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property> <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
<property name="url" value="jdbc:derby://localhost:1527/fhir" /> <property name="url" value="jdbc:derby://localhost:1527#{systemProperties['fhir.db.location']};create=true" />
<property name="username" value="SA"/> <property name="username" value="SA"/>
<property name="password" value="SA"/> <property name="password" value="SA"/>
</bean> </bean>

View File

@ -17,12 +17,12 @@
<ul> <ul>
<li> <li>
View a View a
<a href="http://fhirtest.uhn.ca/search?serverId=home&encoding=json&pretty=true&resource=Patient&param.0.type=string&param.0.name=_id&param.0.0=&resource-search-limit=">list of patients</a> <a href="http://fhirtest.uhn.ca/search?serverId=home&amp;encoding=json&amp;pretty=true&amp;resource=Patient&amp;param.0.type=string&amp;param.0.name=_id&amp;param.0.0=&amp;resource-search-limit=">list of patients</a>
on this server. on this server.
</li> </li>
<li> <li>
Construct a Construct a
<a href="http://fhirtest.uhn.ca/resource?serverId=home&encoding=json&pretty=true&resource=Patient">search query</a> <a href="http://fhirtest.uhn.ca/resource?serverId=home&amp;encoding=json&amp;pretty=true&amp;resource=Patient">search query</a>
on this server. on this server.
</li> </li>
<li> <li>

View File

@ -35,6 +35,10 @@
<servlet> <servlet>
<servlet-name>fhirServlet</servlet-name> <servlet-name>fhirServlet</servlet-name>
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class> <servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
<init-param>
<param-name>ImplementationDescription</param-name>
<param-value>UHN Test Server</param-value>
</init-param>
<load-on-startup>1</load-on-startup> <load-on-startup>1</load-on-startup>
</servlet> </servlet>

View File

@ -75,8 +75,6 @@
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" <xsd:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="xml.xsd"/> schemaLocation="xml.xsd"/>
<xsd:include schemaLocation="javaee_web_services_client_1_3.xsd"/>
<xsd:group name="descriptionGroup"> <xsd:group name="descriptionGroup">
<xsd:annotation> <xsd:annotation>
<xsd:documentation> <xsd:documentation>

View File

@ -23,7 +23,7 @@ public class UhnFhirTestApp {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// new File("target/testdb").mkdirs(); // new File("target/testdb").mkdirs();
System.setProperty("fhir.db.location", "target/testdb"); System.setProperty("fhir.db.location", "/target/testdb");
int myPort = 8888; int myPort = 8888;
Server server = new Server(myPort); Server server = new Server(myPort);

View File

@ -3,7 +3,6 @@
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/> <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="/" 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="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<dependent-module archiveName="hapi-fhir-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base"> <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> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>

View File

@ -31,10 +31,10 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version> <version>3.1.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.to.model.HomeRequest; import ca.uhn.fhir.to.model.HomeRequest;
import ca.uhn.fhir.to.model.ResourceRequest; import ca.uhn.fhir.to.model.ResourceRequest;
import ca.uhn.fhir.to.model.TransactionRequest;
@org.springframework.stereotype.Controller() @org.springframework.stereotype.Controller()
public class Controller { public class Controller {
@ -67,6 +68,39 @@ public class Controller {
@Autowired @Autowired
private TemplateEngine myTemplateEngine; private TemplateEngine myTemplateEngine;
@RequestMapping(value = { "/transaction" })
public String actionTransaction(final TransactionRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
addCommonParams(theRequest, theModel);
GenericClient client = theRequest.newClient(myCtx, myConfig);
String body = preProcessMessageBody(theRequest.getTransactionBody());
Bundle bundle;
try {
if (body.startsWith("{")) {
bundle = myCtx.newJsonParser().parseBundle(body);
} else if (body.startsWith("<")) {
bundle = myCtx.newXmlParser().parseBundle(body);
} else {
theModel.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 "home";
}
} catch (DataFormatException e) {
ourLog.warn("Failed to parse bundle", e);
theModel.put("errorMsg", "Failed to parse transaction bundle body. Error was: " + e.getMessage());
return "home";
}
long start = System.currentTimeMillis();
// client.tr
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, ResultType.RESOURCE, theModel, delay, "Loaded conformance");
return "result";
}
@RequestMapping(value = { "/conformance" }) @RequestMapping(value = { "/conformance" })
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
addCommonParams(theRequest, theModel); addCommonParams(theRequest, theModel);
@ -427,18 +461,7 @@ public class Controller {
return; return;
} }
body = body.trim(); body = preProcessMessageBody(body);
StringBuilder b = new StringBuilder();
for (int i = 0; i < body.length(); i++) {
char nextChar = body.charAt(i);
int nextCharI = nextChar;
if (nextCharI == 65533) {
continue;
}
b.append(nextChar);
}
body = b.toString();
IResource resource; IResource resource;
try { try {
@ -485,6 +508,25 @@ public class Controller {
} }
private String preProcessMessageBody(String theBody) {
if(theBody==null) {
return "";
}
String retVal = theBody.trim();
StringBuilder b = new StringBuilder();
for (int i = 0; i < retVal.length(); i++) {
char nextChar = retVal.charAt(i);
int nextCharI = nextChar;
if (nextCharI == 65533) {
continue;
}
b.append(nextChar);
}
retVal = b.toString();
return retVal;
}
private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) { private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) {
addCommonParams(theRequest, theModel); addCommonParams(theRequest, theModel);

View File

@ -0,0 +1,18 @@
package ca.uhn.fhir.to.model;
import org.springframework.web.bind.annotation.ModelAttribute;
public class TransactionRequest extends HomeRequest {
private String myTransactionBody;
@ModelAttribute("transactionBody")
public String getTransactionBody() {
return myTransactionBody;
}
public void setTransactionBody(String theTransactionBody) {
myTransactionBody = theTransactionBody;
}
}

View File

@ -21,12 +21,5 @@
</bean> </bean>
<bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext"> <bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext">
<property name="restfulClientFactory" ref="restfulClientFactory"/>
</bean>
<bean id="restfulClientFactory" class="ca.uhn.fhir.rest.client.RestfulClientFactory">
<property name="fhirContext" ref="fhirContext"/>
<property name="connectTimeout" value="4000"/>
<property name="socketTimeout" value="10000"/>
</bean> </bean>
</beans> </beans>

View File

@ -31,22 +31,22 @@
<col class="col-xs-7" /> <col class="col-xs-7" />
</colgroup> </colgroup>
<tbody> <tbody>
<tr th:if="${conf.implementation.description} != null and #{!string.isEmpty(conf.implementation.description)}"> <tr th:if="#{!strings.isEmpty(conf.implementation.description.value)}">
<td>Server</td> <td>Server</td>
<td th:utext="'' + ${conf.implementation.description}">HAPI Restful Server</td> <td th:utext="'' + ${conf.implementation.description}">HAPI Restful Server</td>
</tr> </tr>
<tr th:if="#{!strings.isEmpty(conf.software.name.value)} or #{!strings.isEmpty(conf.software.version.value)}">
<td>Software</td>
<td>
<th:block th:utext="'' + ${conf.software.name}"/> - <th:block th:utext="'' + ${conf.software.version}"/>
</td>
</tr>
<tr> <tr>
<td>FHIR Base</td> <td>FHIR Base</td>
<td> <td>
<a th:href="${base}" th:text="${base}"></a> <a th:href="${base}" th:text="${base}"></a>
</td> </td>
</tr> </tr>
<tr th:if="#{string.isEmpty(conf.software.name)} == false and #{string.isEmpty(conf.software.version)} == false">
<td>Software</td>
<td>
<th:block th:utext="'' + ${conf.software.name}"/> - <th:block th:utext="'' + ${conf.software.version}"/>
</td>
</tr>
</tbody> </tbody>
</table> </table>
@ -67,7 +67,7 @@
Retrieve the server's <b>conformance</b> statement. Retrieve the server's <b>conformance</b> statement.
</div> </div>
<div class="row-fluid"> <div class="row-fluid">
<div class="col-sm-2"> <div class="col-sm-2 form-group">
<button type="button" id="fetch-conformance-btn" <button type="button" id="fetch-conformance-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block"> data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<i class="fa fa-dot-circle-o"></i> <i class="fa fa-dot-circle-o"></i>
@ -144,6 +144,61 @@
</div> </div>
<!-- Transaction -->
<br clear="all"/>
<div class="row-fluid">
Post a bundle containing multiple resources to the server and
store all resources within a single atomic transaction.
</div>
<div class="row-fluid">
<div class="col-sm-2">
<button type="button" id="transaction-btn"
data-loading-text="Processing &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<i class="fa fa-files-o"></i>
Transaction
</button>
</div>
<div class='col-sm-10'>
<div class="form-group">
<div class='input-group'>
<div class="input-group-addon">
Bundle
<span class="loadingStar">*</span>
</div>
<textarea class="form-control" id="transaction-body" style="white-space: nowrap; overflow: auto;" placeholder="(place transaction bundle body here)" rows="1">
<th:block th:if="${transactionBundle} != null" th:text="${transactionBundle}"/>
</textarea>
</div>
</div>
</div>
<script type="text/javascript">
var textAreaChanger = function() {
createBodyOriginalHeight = $('#transaction-body').height();
$('#transaction-body').animate({height: "200px"}, 500);
}
$('#transaction-body').focus(textAreaChanger);
$('#transaction-btn').click(
function() {
var btn = $(this);
btn.button('loading');
var id = $('#transaction-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-create-id', value: id }));
var body = $('#transaction-body').val();
btn.append($('<input />', { type: 'hidden', name: 'transactionBody', value: body }));
$("#outerForm").attr("method", "post");
$("#outerForm").attr("action", "create").submit();
});
$( document ).ready(function() {
/* if ($('#resource-create-id').val() != "") {
buttonChanger();
textAreaChanger();
$('#transaction-body').focus();
}
*/ });
</script>
</div>
<!-- Get Tags --> <!-- Get Tags -->
<br clear="all"/> <br clear="all"/>
@ -151,7 +206,7 @@
Show all of the tags currently in use on the server Show all of the tags currently in use on the server
</div> </div>
<div class="row-fluid"> <div class="row-fluid">
<div class="col-sm-2"> <div class="col-sm-2 form-group">
<button type="button" id="get-server-tags-btn" <button type="button" id="get-server-tags-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block"> data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<i class="fa fa-tags"></i> <i class="fa fa-tags"></i>

View File

@ -1,8 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<div th:fragment="banner" class="well"> <div th:fragment="banner" class="well">
<div> <div>
This is a RESTful server tester, which can be used to send This is a RESTful server tester, which can be used to send
requests to, and receive responses from the server. requests to, and receive responses from the server.
</div>
</div> </div>
</html> </html>

View File

@ -59,7 +59,7 @@
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">
<li th:class="${resourceName.empty} ? 'active' : ''"> <li th:class="${resourceName.empty} ? 'active' : ''">
<a href="#" onclick="doAction(this, 'home', null);">Server Home</a> <a href="#" onclick="doAction(this, 'home', null);">Server Home/Actions</a>
</li> </li>
</ul> </ul>

View File

@ -102,6 +102,35 @@
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<versionRange>[0.4-SNAPSHOT,)</versionRange>
<goals>
<goal>generate-structures</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build> </build>
</project> </project>