diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 0cad7a9d756..0bd4ce9d9de 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -70,14 +70,6 @@ test - - - commons-dbcp - commons-dbcp - 1.4 - test - - org.apache.derby @@ -89,7 +81,6 @@ aopalliance aopalliance - 1.0 org.springframework @@ -156,6 +147,11 @@ jetty-util test + + org.apache.commons + commons-dbcp2 + test + diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java index 3b12e61abaf..f07b57d29c9 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java @@ -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; @@ -79,7 +79,7 @@ public class BaseController { if (myConfig.getDebugTemplatesMode()) { myTemplateEngine.getCacheManager().clearAllCaches(); } - + final String serverId = theRequest.getServerIdWithDefault(myConfig); final String serverBase = theRequest.getServerBase(theServletRequest, myConfig); final String serverName = theRequest.getServerName(myConfig); @@ -91,7 +91,7 @@ public class BaseController { theModel.put("pretty", theRequest.getPretty()); theModel.put("_summary", theRequest.get_summary()); theModel.put("serverEntries", myConfig.getIdToServerName()); - + return loadAndAddConf(theServletRequest, theRequest, theModel); } @@ -108,16 +108,28 @@ public class BaseController { return retVal.toArray(new Header[retVal.size()]); } + private Header[] applyHeaderFilters(Map> theAllHeaders) { + ArrayList
retVal = new ArrayList
(); + 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) { return str; } - + StringBuilder b = new StringBuilder(); - + if (theEncodingEnum == EncodingEnum.JSON) { - + boolean inValue = false; boolean inQuote = false; for (int i = 0; i < str.length(); i++) { @@ -171,7 +183,7 @@ public class BaseController { } } } - + } else { boolean inQuote = false; boolean inTag = false; @@ -215,7 +227,7 @@ public class BaseController { } } } - + return b.toString(); } @@ -224,16 +236,16 @@ public class BaseController { if (str == null) { return str; } - + try { str = URLDecoder.decode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { ourLog.error("Should not happen", e); } - + StringBuilder b = new StringBuilder(); b.append(""); - + boolean inParams = false; for (int i = 0; i < str.length(); i++) { char nextChar = str.charAt(i); @@ -261,7 +273,7 @@ public class BaseController { } } } - + if (inParams) { b.append(""); } @@ -291,11 +303,11 @@ public class BaseController { ResultType returnsResource; returnsResource = ResultType.NONE; ourLog.warn("Failed to invoke server", e); - + if (e != null) { theModel.put("errorMsg", "Error: " + e.getMessage()); } - + return returnsResource; } @@ -314,7 +326,7 @@ public class BaseController { private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - + Conformance conformance; try { conformance = (Conformance) client.conformance(); @@ -323,9 +335,9 @@ public class BaseController { theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString()); conformance = new Conformance(); } - + theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance)); - + Map resourceCounts = new HashMap(); long total = 0; for (Rest nextRest : conformance.getRest()) { @@ -339,7 +351,7 @@ public class BaseController { } } theModel.put("resourceCounts", resourceCounts); - + if (total > 0) { for (Rest nextRest : conformance.getRest()) { Collections.sort(nextRest.getResource(), new Comparator() { @@ -364,17 +376,17 @@ public class BaseController { }); } } - + theModel.put("conf", conformance); theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - + return conformance; } private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - + ca.uhn.fhir.model.dstu2.resource.Conformance conformance; try { conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance(); @@ -383,9 +395,9 @@ public class BaseController { theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString()); conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance(); } - + theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance)); - + Map resourceCounts = new HashMap(); long total = 0; for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { @@ -399,7 +411,7 @@ public class BaseController { } } theModel.put("resourceCounts", resourceCounts); - + if (total > 0) { for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { Collections.sort(nextRest.getResource(), new Comparator() { @@ -424,17 +436,17 @@ public class BaseController { }); } } - + theModel.put("conf", conformance); theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - + return conformance; } private IBaseResource loadAndAddConfDstu3(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); - + org.hl7.fhir.dstu3.model.Conformance conformance; try { conformance = client.fetchConformance().ofType(org.hl7.fhir.dstu3.model.Conformance.class).execute(); @@ -443,9 +455,9 @@ public class BaseController { theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString()); conformance = new org.hl7.fhir.dstu3.model.Conformance(); } - + theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance)); - + Map resourceCounts = new HashMap(); long total = 0; for (ConformanceRestComponent nextRest : conformance.getRest()) { @@ -459,7 +471,7 @@ public class BaseController { } } theModel.put("resourceCounts", resourceCounts); - + if (total > 0) { for (ConformanceRestComponent nextRest : conformance.getRest()) { Collections.sort(nextRest.getResource(), new Comparator() { @@ -484,12 +496,13 @@ public class BaseController { }); } } - + theModel.put("conf", conformance); theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED); - + return conformance; } + protected String logPrefix(ModelMap theModel) { return "[server=" + theModel.get("serverId") + "] - "; } @@ -502,7 +515,7 @@ public class BaseController { IResource resource = (IResource) par; retVal = resource.getText().getDiv().getValueAsString(); } else if (par instanceof IDomainResource) { - retVal = ((IDomainResource)par).getText().getDivAsString(); + retVal = ((IDomainResource) par).getText().getDivAsString(); } else { retVal = null; } @@ -518,7 +531,7 @@ public class BaseController { return ""; } String retVal = theBody.trim(); - + StringBuilder b = new StringBuilder(); for (int i = 0; i < retVal.length(); i++) { char nextChar = retVal.charAt(i); @@ -543,30 +556,30 @@ 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()); - + if (lastRequest instanceof HttpEntityEnclosingRequest) { HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity(); if (entity.isRepeatable()) { requestBody = IOUtils.toString(entity.getContent()); } } - + 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; IBaseResource riBundle = null; - + FhirContext context = getContext(theRequest); if (ctEnum == null) { resultDescription.append("Non-FHIR response"); @@ -601,70 +614,70 @@ public class BaseController { 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]; - + theModelMap.put("outcomeDescription", outcomeDescription); theModelMap.put("resultDescription", resultDescription.toString()); theModelMap.put("action", action); theModelMap.put("bundle", bundle); theModelMap.put("riBundle", riBundle); theModelMap.put("resultStatus", resultStatus); - + theModelMap.put("requestUrl", requestUrl); theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl)); - + String requestBodyText = format(requestBody, ctEnum); theModelMap.put("requestBody", requestBodyText); - + String resultBodyText = format(resultBody, ctEnum); theModelMap.put("resultBody", resultBodyText); - + theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000); theModelMap.put("requestHeaders", requestHeaders); theModelMap.put("responseHeaders", responseHeaders); theModelMap.put("narrative", narrativeString); theModelMap.put("latencyMs", theLatency); - + } catch (Exception e) { ourLog.error("Failure during processing", e); theModelMap.put("errorMsg", "Error during processing: " + e.getMessage()); } - + } 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; } - + public HttpResponse getLastResponse() { return myLastResponse; } - + public String getLastResponseBody() { return myResponseBody; } - + @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) { final byte[] bytes; @@ -673,37 +686,37 @@ public class BaseController { } catch (IllegalStateException e) { throw new InternalErrorException(e); } - + myResponseBody = new String(bytes, "UTF-8"); myLastResponse.setEntity(new MyEntityWrapper(respEntity, bytes)); } } - + private static class MyEntityWrapper extends HttpEntityWrapper { - + private byte[] myBytes; - + public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) { super(theWrappedEntity); myBytes = theBytes; } - + @Override public InputStream getContent() throws IOException { return new ByteArrayInputStream(myBytes); } - + @Override public void writeTo(OutputStream theOutstream) throws IOException { theOutstream.write(myBytes); } - + } - + } protected enum ResultType { - BUNDLE, NONE, RESOURCE, TAGLIST - } + BUNDLE, NONE, RESOURCE, TAGLIST + } } \ No newline at end of file diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html index 13794d68e9c..32fd57522a9 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html @@ -1,7 +1,9 @@ - - RESTful Tester + + + <th:block th:include="tmpl-head :: head" /> + <script th:include="tmpl-buttonclick-handler :: handler" /> </head> <body> @@ -68,18 +70,17 @@ </div> <div class="row-fluid"> <div class="col-sm-3 form-group"> - <button type="button" id="fetch-conformance-btn" - data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" 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'); - $("#outerForm").attr("action", "conformance").submit(); - }); + function() { + handleActionButtonClick($(this)); + $("#outerForm").attr("action", "conformance").submit(); + }); </script> </div> </div> @@ -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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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> diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html index 03a6225fc99..c1fd3f50cd8 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html @@ -1,7 +1,9 @@ <!DOCTYPE html> <html lang="en"> - <head th:include="tmpl-head :: head"> - <title>RESTful Tester + + + <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 <i class='fa fa-spinner fa-spin'/>"> + <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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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 <i class='fa fa-spinner fa-spin'/>" 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(); diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/result.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/result.html index 5a6ce6eaf53..4d9feaa94ea 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/result.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/result.html @@ -1,7 +1,9 @@ <!DOCTYPE html> <html lang="en"> - <head th:include="tmpl-head :: head"> - <title>RESTful Tester + + + <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 <i class='fa fa-spinner fa-spin'/>"> + 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 <i class='fa fa-spinner fa-spin'/>"> + <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 <i class='fa fa-spinner fa-spin'/>" ><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 <i class='fa fa-spinner fa-spin'/>" ></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 <i class='fa fa-spinner fa-spin'/>"> + 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 <i class='fa fa-spinner fa-spin'/>"> + <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 <i class='fa fa-spinner fa-spin'/>" ><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 <i class='fa fa-spinner fa-spin'/>" ></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> diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-buttonclick-handler.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-buttonclick-handler.html new file mode 100644 index 00000000000..cb2652d1c10 --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-buttonclick-handler.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html lang="en"> + <script th:fragment="handler"> + function handleActionButtonClick(button) { + // nothing for now + } + </script> +</html> diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-left.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-left.html index 4e1a309fa7d..a0c3badf201 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-left.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-navbar-left.html @@ -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> diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-queries.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-queries.html index ca4ec740456..67d56ad3bfd 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-queries.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/tmpl-queries.html @@ -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 <i class='fa fa-spinner fa-spin'/>"> + <button type="button" th:id="'search-btn-' + ${queryIterStat.index}" class="btn btn-primary btn-block">  <span class="glyphicon glyphicon-search"></span> Execute   @@ -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) { diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/window-title.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/window-title.html new file mode 100644 index 00000000000..3f4c8a5689a --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/window-title.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html lang="en"> + <title th:fragment="home">HAPI FHIR + <th:block th:text="${resourceName}"/> - HAPI FHIR + Results - HAPI FHIR + diff --git a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirServerConfig.java b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirServerConfig.java new file mode 100644 index 00000000000..d1407abf250 --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/FhirServerConfig.java @@ -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; + } + +} diff --git a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java index 44fcd73a2ea..0dbc91fc634 100644 --- a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java +++ b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java @@ -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 rpsDev = (List) 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 rpsDstu1 = (List) 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 rpsDstu1 = (List) 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(); diff --git a/hapi-fhir-testpage-overlay/src/test/resources/hapi-fhir-tester-config.xml b/hapi-fhir-testpage-overlay/src/test/resources/hapi-fhir-tester-config.xml new file mode 100644 index 00000000000..16d170463e4 --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/test/resources/hapi-fhir-tester-config.xml @@ -0,0 +1,27 @@ + + + + + + uhn , DSTU2 , UHN , http://fhirtest.uhn.ca/baseDstu2 + home_d2 , DSTU2 , Localhost Server DSTU2 , http://localhost:8887/fhir/contextDstu2 + hi , DSTU1 , Health Intersections , http://fhir.healthintersections.com.au/open + furore , DSTU1 , Spark - Furore Reference Server , http://spark.furore.com/fhir + blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir + oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190 + fhirbase , DSTU1 , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-testpage-overlay/src/test/resources/web.xml b/hapi-fhir-testpage-overlay/src/test/resources/web.xml new file mode 100644 index 00000000000..f1df4d34fa9 --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/test/resources/web.xml @@ -0,0 +1,44 @@ + + + + + org.springframework.web.context.ContextLoaderListener + + + + contextConfigLocation + + /WEB-INF/hapi-fhir-tester-application-context.xml + classpath:hapi-fhir-tester-config.xml + + + + + + spring + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + + /WEB-INF/hapi-fhir-tester-application-context.xml + classpath:hapi-fhir-tester-config.xml + + + 1 + + + + spring + / + + + + + *.jsp + true + + + + \ No newline at end of file diff --git a/hapi-fhir-testpage-overlay/src/test/resources/web_.xml b/hapi-fhir-testpage-overlay/src/test/resources/web_.xml new file mode 100644 index 00000000000..db9662dabec --- /dev/null +++ b/hapi-fhir-testpage-overlay/src/test/resources/web_.xml @@ -0,0 +1,27 @@ + + + + + + spring + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + + + + 2 + + + + + spring + /* + + + + + + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c9da934ded4..f7f38ef2622 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -150,6 +150,14 @@ encoded correctly in XML, and sometimes not parsed correctly in JSON. Thanks to Bill de Beaubien for reporting! + + 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! +