Fix #280 - Don't leave web testing UI buttons disabled when you return to a page via the back button

This commit is contained in:
jamesagnew 2016-03-01 07:15:02 -05:00
parent 496d866f48
commit c44b23f493
15 changed files with 418 additions and 177 deletions

View File

@ -70,14 +70,6 @@
<scope>test</scope>
</dependency>
<!-- Database -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<!-- Test Database -->
<dependency>
<groupId>org.apache.derby</groupId>
@ -89,7 +81,6 @@
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
@ -156,6 +147,11 @@
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -28,11 +28,11 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.HttpEntityWrapper;
import org.hl7.fhir.dstu3.model.DecimalType;
import org.hl7.fhir.dstu3.model.Extension;
import org.apache.http.message.BasicHeader;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestComponent;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestResourceComponent;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.dstu3.model.DecimalType;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.springframework.beans.factory.annotation.Autowired;
@ -43,15 +43,15 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest;
import ca.uhn.fhir.rest.client.apache.ApacheHttpResponse;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -108,6 +108,18 @@ public class BaseController {
return retVal.toArray(new Header[retVal.size()]);
}
private Header[] applyHeaderFilters(Map<String, List<String>> theAllHeaders) {
ArrayList<Header> retVal = new ArrayList<Header>();
for (String nextKey : theAllHeaders.keySet()) {
for (String nextValue : theAllHeaders.get(nextKey)) {
if (myFilterHeaders == null || !myFilterHeaders.contains(nextKey.toLowerCase())) {
retVal.add(new BasicHeader(nextKey, nextValue));
}
}
}
return retVal.toArray(new Header[retVal.size()]);
}
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
String str = StringEscapeUtils.escapeHtml4(theResultBody);
if (str == null || theEncodingEnum == null) {
@ -490,6 +502,7 @@ public class BaseController {
return conformance;
}
protected String logPrefix(ModelMap theModel) {
return "[server=" + theModel.get("serverId") + "] - ";
}
@ -543,11 +556,11 @@ public class BaseController {
protected void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription, CaptureInterceptor theInterceptor, HomeRequest theRequest) {
try {
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
ApacheHttpRequest lastRequest = theInterceptor.getLastRequest();
HttpResponse lastResponse = theInterceptor.getLastResponse();
String requestBody = null;
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
String action = lastRequest != null ? lastRequest.getMethod() : null;
String requestUrl = lastRequest != null ? lastRequest.getApacheRequest().getURI().toASCIIString() : null;
String action = lastRequest != null ? lastRequest.getApacheRequest().getMethod() : null;
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
@ -638,11 +651,11 @@ public class BaseController {
public static class CaptureInterceptor implements IClientInterceptor {
private HttpRequestBase myLastRequest;
private ApacheHttpRequest myLastRequest;
private HttpResponse myLastResponse;
private String myResponseBody;
public HttpRequestBase getLastRequest() {
public ApacheHttpRequest getLastRequest() {
return myLastRequest;
}
@ -657,13 +670,13 @@ public class BaseController {
@Override
public void interceptRequest(IHttpRequest theRequest) {
assert myLastRequest == null;
myLastRequest = (HttpRequestBase) theRequest;
myLastRequest = (ApacheHttpRequest) theRequest;
}
@Override
public void interceptResponse(IHttpResponse theResponse) throws IOException {
assert myLastResponse == null;
myLastResponse = (HttpResponse) theResponse;
myLastResponse = ((ApacheHttpResponse) theResponse).getResponse();
HttpEntity respEntity = myLastResponse.getEntity();
if (respEntity != null) {

View File

@ -1,7 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head th:include="tmpl-head :: head">
<title>RESTful Tester</title>
<head>
<title th:include="window-title :: home" />
<th:block th:include="tmpl-head :: head" />
<script th:include="tmpl-buttonclick-handler :: handler" />
</head>
<body>
@ -68,16 +70,15 @@
</div>
<div class="row-fluid">
<div class="col-sm-3 form-group">
<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">
<a type="button" id="fetch-conformance-btn"
class="btn btn-primary btn-block">
<i class="fa fa-dot-circle-o"></i>
Conformance
</button>
</a>
<script type="text/javascript">
$('#fetch-conformance-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
$("#outerForm").attr("action", "conformance").submit();
});
</script>
@ -94,7 +95,7 @@
<div class="row-fluid top-buffer">
<div class="col-sm-3">
<button type="button" id="server-history-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
class="btn btn-primary btn-block">
<i class="fa fa-calendar"></i>
History
</button>
@ -134,7 +135,7 @@
$('#server-history-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var limit = $('#server-history-limit').val();
if (limit != null) btn.append($('<input />', { type: 'hidden', name: 'limit', value: limit }));
var since = $('#server-history-since').val();
@ -154,8 +155,7 @@
</div>
<div class="row-fluid">
<div class="col-sm-3">
<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">
<button type="button" id="transaction-btn" class="btn btn-primary btn-block">
<i class="fa fa-files-o"></i>
Transaction
</button>
@ -182,7 +182,7 @@
$('#transaction-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#transaction-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-create-id', value: id }));
var body = $('#transaction-body').val();
@ -208,8 +208,7 @@
</div>
<div class="row-fluid">
<div class="col-sm-3 form-group">
<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">
<button type="button" id="get-server-tags-btn" class="btn btn-primary btn-block">
<i class="fa fa-tags"></i>
Get Tags
</button>
@ -217,7 +216,7 @@
$('#get-server-tags-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
$("#outerForm").attr("action", "get-tags").submit();
});
</script>

View File

@ -1,7 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head th:include="tmpl-head :: head">
<title>RESTful Tester</title>
<head>
<title th:include="window-title :: resource" />
<th:block th:include="tmpl-head :: head" />
<script th:include="tmpl-buttonclick-handler :: handler" />
</head>
<body>
@ -91,8 +93,7 @@
<div class="container-fluid">
<div class="row-fluid">
<div class="col-sm-2" style="padding-left: 0px;">
<button type="button" id="search-btn" class="btn btn-primary btn-block"
data-loading-text="Searching &lt;i class='fa fa-spinner fa-spin'/&gt;">
<button type="button" id="search-btn" class="btn btn-primary btn-block">
<span class="glyphicon glyphicon-search"></span>
Search
</button>
@ -101,7 +102,7 @@
<script type="text/javascript">
$('#search-btn').click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
$('#tab-search').find('input').each(function() {
if (this.id) {
if (this.id.substring(0,4) == 'inc_') {
@ -248,7 +249,7 @@
<div class="row-fluid">
<div class="col-sm-2">
<button type="button" id="read-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
class="btn btn-primary btn-block">
<i class="fa fa-book"></i>
Read
</button>
@ -278,7 +279,7 @@
$('#read-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#read-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'id', value: id }));
var vid = $('#read-vid').val();
@ -298,8 +299,7 @@
</div>
<div class="row-fluid top-buffer">
<div class="col-sm-2">
<button type="button" id="resource-history-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block"
<button type="button" id="resource-history-btn" class="btn btn-primary btn-block"
name="action-history-type">
<i class="fa fa-calendar"></i>
History
@ -350,7 +350,7 @@
$('#resource-history-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var limit = $('#resource-history-limit').val();
if (limit != null) btn.append($('<input />', { type: 'hidden', name: 'limit', value: limit }));
var since = $('#resource-history-since').val();
@ -370,8 +370,7 @@
</div>
<div class="row-fluid top-buffer">
<div class="col-sm-2">
<button type="button" id="resource-delete-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<button type="button" id="resource-delete-btn" class="btn btn-primary btn-block">
<i class="fa fa-trash-o"></i>
Delete
</button>
@ -391,7 +390,7 @@
$('#resource-delete-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#resource-delete-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-delete-id', value: id }));
$("#outerForm").attr("action", "delete").submit();
@ -409,7 +408,7 @@
<div class="row-fluid top-buffer">
<div class="col-sm-2">
<button type="button" id="resource-create-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
class="btn btn-primary btn-block">
<i class="fa fa-send"></i>
Create
</button>
@ -445,7 +444,7 @@
$('#resource-create-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#resource-create-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-create-id', value: id }));
var body = $('#resource-create-body').val();
@ -470,8 +469,7 @@
</div>
<div class="row-fluid top-buffer">
<div class="col-sm-2">
<button type="button" id="resource-update-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<button type="button" id="resource-update-btn" class="btn btn-primary btn-block">
<i class="fa fa-send"></i>
Update
</button>
@ -509,7 +507,7 @@
$('#resource-update-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#resource-update-id').val();
// Note we're using resource-create-id even though this is an update because
// the controller expects that...
@ -538,8 +536,7 @@
</div>
<div class="row-fluid top-buffer">
<div class="col-sm-2">
<button type="button" id="resource-validate-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<button type="button" id="resource-validate-btn" class="btn btn-primary btn-block">
<i class="fa fa-thumbs-up"></i>
Validate
</button>
@ -564,7 +561,7 @@
$('#resource-validate-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var body = $('#resource-validate-body').val();
btn.append($('<input />', { type: 'hidden', name: 'resource-validate-body', value: body }));
$("#outerForm").attr("action", "validate").attr("method", "POST").submit();
@ -591,8 +588,7 @@
</div>
<div class="row-fluid">
<div class="col-sm-2">
<button type="button" id="get-resource-tags-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" class="btn btn-primary btn-block">
<button type="button" id="get-resource-tags-btn" class="btn btn-primary btn-block">
<i class="fa fa-tags"></i>
Get Tags
</button>
@ -621,7 +617,7 @@
$('#get-resource-tags-btn').click(
function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var id = $('#resource-tags-id').val();
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-tags-id', value: id }));
var vid = $('#resource-tags-vid').val();

View File

@ -1,7 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head th:include="tmpl-head :: head">
<title>RESTful Tester</title>
<head>
<title th:include="window-title :: result" />
<th:block th:include="tmpl-head :: head" />
<script th:include="tmpl-buttonclick-handler :: handler" />
</head>
<body>
@ -136,7 +138,7 @@
<!-- Prev/Next Page Buttons -->
<button class="btn btn-success btn-xs" type="button" id="page-prev-btn"
style="margin-left: 15px;" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;">
style="margin-left: 15px;">
<span class="glyphicon glyphicon-step-backward"></span>
Prev Page
</button>
@ -146,15 +148,14 @@
}
$('#page-prev-btn').click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
btn.append($('<input />', { type: 'hidden', name: 'page-url', value: '<th:block th:text="${bundle.linkPrevious}"/>' }));
$("#outerForm").attr("action", "page").submit();
});
</script>
<button class="btn btn-success btn-xs" type="button" id="page-next-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;">
<button class="btn btn-success btn-xs" type="button" id="page-next-btn">
<span class="glyphicon glyphicon-step-forward"></span>
Next Page
</button>
@ -164,7 +165,7 @@
}
$('#page-next-btn').click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
btn.append($('<input />', { type: 'hidden', name: 'page-url', value: '<th:block th:text="${bundle.linkNext}"/>' }));
$("#outerForm").attr("action", "page").submit();
});
@ -193,8 +194,8 @@
<tr th:each="entry : ${bundle.entries}">
<td style="white-space: nowrap;">
<th:block th:if="${entry.resource} != null">
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart,'')} + '\');'" type="submit" name="action" value="read" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" ><i class="fa fa-book"></i> Read</button>
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" ></i> Update</button>
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart,'')} + '\');'" type="submit" name="action" value="read"><i class="fa fa-book"></i> Read</button>
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil"></i> Update</button>
</th:block>
</td>
<td>
@ -247,7 +248,7 @@
<!-- Prev/Next Page Buttons -->
<button class="btn btn-success btn-xs" type="button" id="page-prev-btn"
style="margin-left: 15px;" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;">
style="margin-left: 15px;">
<span class="glyphicon glyphicon-step-backward"></span>
Prev Page
</button>
@ -257,15 +258,14 @@
}
$('#page-prev-btn').click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
btn.append($('<input />', { type: 'hidden', name: 'page-url', value: '<th:block th:if="${riBundle.getLink('prev') != null}" th:text="${riBundle.getLink('prev').url}"/>' }));
$("#outerForm").attr("action", "page").submit();
});
</script>
<button class="btn btn-success btn-xs" type="button" id="page-next-btn"
data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;">
<button class="btn btn-success btn-xs" type="button" id="page-next-btn">
<span class="glyphicon glyphicon-step-forward"></span>
Next Page
</button>
@ -275,7 +275,7 @@
}
$('#page-next-btn').click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
btn.append($('<input />', { type: 'hidden', name: 'page-url', value: '<th:block th:if="${riBundle.getLink('next') != null}" th:text="${riBundle.getLink('next').url}"/>' }));
$("#outerForm").attr("action", "page").submit();
});
@ -303,8 +303,8 @@
<tr th:each="entry : ${riBundle.entry}">
<td style="white-space: nowrap;">
<th:block th:if="${entry.resource} != null">
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.idElement.resourceType} + '\', \'' + ${entry.resource.idElement.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.idElement.versionIdPart,'')} + '\');'" type="submit" name="action" value="read" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" ><i class="fa fa-book"></i> Read</button>
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.idElement.resourceType} + '\', \'' + ${entry.resource.idElement.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.idElement.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil" data-loading-text="Loading &lt;i class='fa fa-spinner fa-spin'/&gt;" ></i> Update</button>
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.idElement.resourceType} + '\', \'' + ${entry.resource.idElement.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.idElement.versionIdPart,'')} + '\');'" type="submit" name="action" value="read"><i class="fa fa-book"></i> Read</button>
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.idElement.resourceType} + '\', \'' + ${entry.resource.idElement.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.idElement.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil"></i> Update</button>
</th:block>
</td>
<td>

View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<script th:fragment="handler">
function handleActionButtonClick(button) {
// nothing for now
}
</script>
</html>

View File

@ -119,8 +119,8 @@
<h4>Resources</h4>
<ul class="nav nav-sidebar">
<th:block th:unless="${conf.rest.empty}" th:each="resource, resIterStat : ${conf.rest[0].resource}">
<ul class="nav nav-sidebar" th:unless="${conf.rest.empty}">
<th:block th:each="resource, resIterStat : ${conf.rest[0].resource}">
<li th:class="${resourceName} == ${resource.typeElement.valueAsString} ? 'active' : ''">
<a href="#" th:onclick="'doAction(this, \'resource\', \'' + ${resource.typeElement.valueAsString} + '\');'">
<th:block th:text="${resource.typeElement.valueAsString}" >Patient</th:block>

View File

@ -5,8 +5,7 @@
<div class="panel-heading">
<div class="pull-right">
<button type="button" th:id="'search-btn-' + ${queryIterStat.index}" class="btn btn-primary btn-block"
data-loading-text="Searching &lt;i class='fa fa-spinner fa-spin'/&gt;">
<button type="button" th:id="'search-btn-' + ${queryIterStat.index}" class="btn btn-primary btn-block">
&nbsp;<span class="glyphicon glyphicon-search"></span>
Execute
&nbsp;
@ -15,7 +14,7 @@
var searchButtonName = '<th:block th:text="'search-btn-' + ${queryIterStat.index}"/>';
$('#' + searchButtonName).click(function() {
var btn = $(this);
btn.button('loading');
handleActionButtonClick($(this));
var searchDiv = '<th:block th:text="'search-div-' + ${queryIterStat.index}"/>';
$('#' + searchDiv).find('input').each(function() {
if (this.id) {

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<title th:fragment="home">HAPI FHIR</title>
<title th:fragment="resource"><th:block th:text="${resourceName}"/> - HAPI FHIR</title>
<title th:fragment="result">Results - HAPI FHIR</title>
</html>

View File

@ -0,0 +1,121 @@
package ca.uhn.fhir.jpa.test;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
@Configuration
@EnableTransactionManagement()
public class FhirServerConfig extends BaseJavaConfigDstu2 {
/**
* Configure FHIR properties around the the JPA server via this bean
*/
@Bean()
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
retVal.setSubscriptionEnabled(true);
retVal.setSubscriptionPollDelay(5000);
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
retVal.setAllowMultipleDelete(true);
return retVal;
}
/**
* The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
* directory called "jpaserver_derby_files".
*
* A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
*/
@Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource();
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:directory:target/jpaserver_derby_files;create=true");
retVal.setUsername("");
retVal.setPassword("");
return retVal;
}
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
retVal.setPersistenceUnitName("HAPI_PU");
retVal.setDataSource(dataSource());
retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
retVal.setPersistenceProvider(new HibernatePersistenceProvider());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName());
extraProperties.put("hibernate.format_sql", "true");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
return extraProperties;
}
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
*/
public IServerInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
"Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]");
retVal.setLogExceptions(true);
retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
return retVal;
}
/**
* This interceptor adds some pretty syntax highlighting in responses when a browser is detected
*/
@Bean(autowire = Autowire.BY_TYPE)
public IServerInterceptor responseHighlighterInterceptor() {
ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor();
return retVal;
}
@Bean(autowire = Autowire.BY_TYPE)
public IServerInterceptor subscriptionSecurityInterceptor() {
SubscriptionsRequireManualActivationInterceptorDstu2 retVal = new SubscriptionsRequireManualActivationInterceptorDstu2();
return retVal;
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
}

View File

@ -1,15 +1,13 @@
package ca.uhn.fhir.jpa.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
@ -39,7 +37,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
public class OverlayTestApp {
private static ClassPathXmlApplicationContext ourAppCtx;
private static AnnotationConfigApplicationContext ourAppCtx;
@SuppressWarnings({ "unchecked" })
public static void main(String[] args) throws Exception {
@ -50,7 +48,7 @@ public class OverlayTestApp {
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setDescriptor("src/main/webapp/WEB-INF/web.xml");
root.setDescriptor("src/test/resources/web.xml");
root.setResourceBase("src/main/webapp");
root.setParentLoaderPriority(true);
@ -61,10 +59,9 @@ public class OverlayTestApp {
}
ourAppCtx = new ClassPathXmlApplicationContext(
"hapi-fhir-server-resourceproviders-dstu2.xml",
"hapi-fhir-server-resourceproviders-dstu1.xml",
"fhir-jpabase-spring-test-config.xml");
if (true) {return;}
ourAppCtx = new AnnotationConfigApplicationContext(FhirServerConfig.class);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
@ -72,37 +69,37 @@ public class OverlayTestApp {
* DSTU2 resources
*/
RestfulServer restServerDev = new RestfulServer();
restServerDev.setPagingProvider(new FifoMemoryPagingProvider(10));
restServerDev.setImplementationDescription("This is a great server!!!!");
restServerDev.setFhirContext(ourAppCtx.getBean("myFhirContextDstu2", FhirContext.class));
RestfulServer restServerDstu2 = new RestfulServer();
restServerDstu2.setPagingProvider(new FifoMemoryPagingProvider(10));
restServerDstu2.setImplementationDescription("This is a great server!!!!");
restServerDstu2.setFhirContext(ourAppCtx.getBean("myFhirContextDstu2", FhirContext.class));
List<IResourceProvider> rpsDev = (List<IResourceProvider>) ourAppCtx.getBean("myResourceProvidersDstu2", List.class);
restServerDev.setResourceProviders(rpsDev);
restServerDstu2.setResourceProviders(rpsDev);
JpaSystemProviderDstu2 systemProvDev = (JpaSystemProviderDstu2) ourAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
restServerDev.setPlainProviders(systemProvDev);
restServerDstu2.setPlainProviders(systemProvDev);
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServerDev);
servletHolder.setServlet(restServerDstu2);
proxyHandler.addServlet(servletHolder, "/fhir/contextDstu2/*");
/*
* DSTU resources
*/
RestfulServer restServerDstu1 = new RestfulServer();
restServerDstu1.setPagingProvider(new FifoMemoryPagingProvider(10));
restServerDstu1.setImplementationDescription("This is a great server!!!!");
restServerDstu1.setFhirContext(ourAppCtx.getBean("myFhirContextDstu1", FhirContext.class));
List<IResourceProvider> rpsDstu1 = (List<IResourceProvider>) ourAppCtx.getBean("myResourceProvidersDstu1", List.class);
restServerDstu1.setResourceProviders(rpsDstu1);
JpaSystemProviderDstu1 systemProvDstu1 = (JpaSystemProviderDstu1) ourAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class);
restServerDstu1.setPlainProviders(systemProvDstu1);
servletHolder = new ServletHolder();
servletHolder.setServlet(restServerDstu1);
proxyHandler.addServlet(servletHolder, "/fhir/contextDstu1/*");
// RestfulServer restServerDstu1 = new RestfulServer();
// restServerDstu1.setPagingProvider(new FifoMemoryPagingProvider(10));
// restServerDstu1.setImplementationDescription("This is a great server!!!!");
// restServerDstu1.setFhirContext(ourAppCtx.getBean("myFhirContextDstu1", FhirContext.class));
// List<IResourceProvider> rpsDstu1 = (List<IResourceProvider>) ourAppCtx.getBean("myResourceProvidersDstu1", List.class);
// restServerDstu1.setResourceProviders(rpsDstu1);
//
// JpaSystemProviderDstu1 systemProvDstu1 = (JpaSystemProviderDstu1) ourAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class);
// restServerDstu1.setPlainProviders(systemProvDstu1);
//
// servletHolder = new ServletHolder();
// servletHolder.setServlet(restServerDstu1);
// proxyHandler.addServlet(servletHolder, "/fhir/contextDstu1/*");
int port = 8887;
Server server = new Server(port);
@ -115,7 +112,7 @@ public class OverlayTestApp {
if (true) {
String base = "http://localhost:" + port + "/fhir/contextDstu1";
IGenericClient client = restServerDstu1.getFhirContext().newRestfulGenericClient(base);
IGenericClient client = restServerDstu2.getFhirContext().newRestfulGenericClient(base);
client.setLogRequestAndResponse(true);
Organization o1 = new Organization();

View File

@ -0,0 +1,27 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<bean class="ca.uhn.fhir.to.TesterConfig">
<property name="servers">
<list>
<value>uhn , DSTU2 , UHN , http://fhirtest.uhn.ca/baseDstu2</value>
<value>home_d2 , DSTU2 , Localhost Server DSTU2 , http://localhost:8887/fhir/contextDstu2</value>
<value>hi , DSTU1 , Health Intersections , http://fhir.healthintersections.com.au/open</value>
<value>furore , DSTU1 , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
<value>blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
<value>oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190</value>
<value>fhirbase , DSTU1 , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value>
</list>
</property>
</bean>
<bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext">
</bean>
</beans>

View File

@ -0,0 +1,44 @@
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/hapi-fhir-tester-application-context.xml
classpath:hapi-fhir-tester-config.xml
</param-value>
</context-param>
<!-- Processes application requests -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/hapi-fhir-tester-application-context.xml
classpath:hapi-fhir-tester-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>
</web-app>

View File

@ -0,0 +1,27 @@
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ./xsd/web-app_3_0.xsd">
<!-- Servlets -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -150,6 +150,14 @@
encoded correctly in XML, and sometimes not parsed correctly in JSON.
Thanks to Bill de Beaubien for reporting!
</action>
<action type="fix" issue="280">
The Web Testing UI has long had an issue where if you click on a button which
navigates to a new page (e.g. search, read, etc) and then click the back button
to return to the original page, the button you clicked remains disabled and can't
be clicked again (on Firefox and Safari). This is now fixed. Unfortunately the fix means that the
buttom will no longer show a "loading" spinner, but there doesn't seem to
be another way of fixing this. Thanks to Mark Scrimshire for reporting!
</action>
</release>
<release version="1.4" date="2016-02-04">
<action type="add">