Correct URI loading in testpage-overlay (#1565)

* Clean up tests

* Try to fix intermittent

* Fix URI issue

* Add changelog
This commit is contained in:
James Agnew 2019-10-25 16:14:06 -04:00 committed by GitHub
parent bf3af43f32
commit 403a3d1889
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 193 additions and 82 deletions

View File

@ -155,6 +155,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
@Override
public void cancelAllActiveSearches() {
for (SearchTask next : myIdToSearchTask.values()) {
ourLog.info("Requesting immediate abort of search: {}", next.getSearch().getUuid());
next.requestImmediateAbort();
AsyncUtil.awaitLatchAndIgnoreInterrupt(next.getCompletionLatch(), 30, TimeUnit.SECONDS);
}
@ -631,6 +632,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
Integer awaitInitialSync() {
ourLog.trace("Awaiting initial sync");
do {
ourLog.trace("Search {} aborted: {}", getSearch().getUuid(), !isNotAborted());
if (AsyncUtil.awaitLatchAndThrowInternalErrorExceptionOnInterrupt(getInitialCollectionLatch(), 250L, TimeUnit.MILLISECONDS)) {
break;
}

View File

@ -8,8 +8,10 @@ import ca.uhn.fhir.rest.client.apache.ResourceEntity;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
@ -96,7 +98,7 @@ public class BulkDataExportProviderTest {
}
@Test
public void testSuccessfulInitiateBulkRequest() throws IOException {
public void testSuccessfulInitiateBulkRequest_Post() throws IOException {
IBulkDataExportSvc.JobInfo jobInfo = new IBulkDataExportSvc.JobInfo()
.setJobId(A_JOB_ID);
@ -110,9 +112,12 @@ public class BulkDataExportProviderTest {
input.addParameter(JpaConstants.PARAM_EXPORT_SINCE, now);
input.addParameter(JpaConstants.PARAM_EXPORT_TYPE_FILTER, new StringType("Patient?identifier=foo"));
ourLog.info(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input));
HttpPost post = new HttpPost("http://localhost:" + myPort + "/" + JpaConstants.OPERATION_EXPORT);
post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC);
post.setEntity(new ResourceEntity(myCtx, input));
ourLog.info("Request: {}", post);
try (CloseableHttpResponse response = myClient.execute(post)) {
ourLog.info("Response: {}", response.toString());
@ -129,6 +134,40 @@ public class BulkDataExportProviderTest {
}
@Test
public void testSuccessfulInitiateBulkRequest_Get() throws IOException {
IBulkDataExportSvc.JobInfo jobInfo = new IBulkDataExportSvc.JobInfo()
.setJobId(A_JOB_ID);
when(myBulkDataExportSvc.submitJob(any(), any(), any(), any())).thenReturn(jobInfo);
InstantType now = InstantType.now();
String url = "http://localhost:" + myPort + "/" + JpaConstants.OPERATION_EXPORT
+ "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON)
+ "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner")
+ "&" + JpaConstants.PARAM_EXPORT_SINCE+ "="+ UrlUtil.escapeUrlParam(now.getValueAsString())
+ "&" + JpaConstants.PARAM_EXPORT_TYPE_FILTER + "=" + UrlUtil.escapeUrlParam("Patient?identifier=foo");
HttpGet get = new HttpGet(url);
get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC);
ourLog.info("Request: {}", url);
try (CloseableHttpResponse response = myClient.execute(get)) {
ourLog.info("Response: {}", response.toString());
assertEquals(202, response.getStatusLine().getStatusCode());
assertEquals("Accepted", response.getStatusLine().getReasonPhrase());
assertEquals("http://localhost:" + myPort + "/$export-poll-status?_jobId=" + A_JOB_ID, response.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue());
}
verify(myBulkDataExportSvc, times(1)).submitJob(myOutputFormatCaptor.capture(), myResourceTypesCaptor.capture(), mySinceCaptor.capture(), myFiltersCaptor.capture());
assertEquals(Constants.CT_FHIR_NDJSON, myOutputFormatCaptor.getValue());
assertThat(myResourceTypesCaptor.getValue(), containsInAnyOrder("Patient", "Practitioner"));
assertThat(mySinceCaptor.getValue(), notNullValue());
assertThat(myFiltersCaptor.getValue(), containsInAnyOrder("Patient?identifier=foo"));
}
@Test
public void testPollForStatus_BUILDING() throws IOException {

View File

@ -33,6 +33,7 @@ import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.DEFAULT_CUTOFF_SLACK;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
@ -165,13 +166,10 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test {
DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 2100);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertFalse("Search 1 still exists", mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).isPresent());
assertFalse("Search 3 still exists", mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).isPresent());
}
});
await().until(()-> newTxTemplate().execute(t -> !mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).isPresent()));
await().until(()-> newTxTemplate().execute(t -> !mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).isPresent()));
}

View File

@ -26,9 +26,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
@ -39,8 +37,12 @@ import org.springframework.transaction.TransactionStatus;
import javax.persistence.EntityManager;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
@ -108,9 +110,9 @@ public class SearchCoordinatorSvcImplTest {
}).when(myCallingDao).injectDependenciesIntoBundleProvider(any(PersistedJpaBundleProvider.class));
}
private List<Long> createPidSequence(int from, int to) {
private List<Long> createPidSequence(int to) {
List<Long> pids = new ArrayList<>();
for (long i = from; i < to; i++) {
for (long i = 10; i < to; i++) {
pids.add(i);
}
return pids;
@ -134,7 +136,7 @@ public class SearchCoordinatorSvcImplTest {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
IResultIterator iter = new FailAfterNIterator(new SlowIterator(pids.iterator(), 2), 300);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(iter);
@ -166,7 +168,7 @@ public class SearchCoordinatorSvcImplTest {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
SlowIterator iter = new SlowIterator(pids.iterator(), 1);
when(mySearchBuilder.createQuery(any(), any(), any())).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
@ -203,9 +205,7 @@ public class SearchCoordinatorSvcImplTest {
myCurrentSearch = search;
return search;
});
when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> {
return Optional.ofNullable(myCurrentSearch);
});
when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> Optional.ofNullable(myCurrentSearch));
IFhirResourceDao dao = myCallingDao;
when(myDaoRegistry.getResourceDao(any(String.class))).thenReturn(dao);
@ -274,7 +274,7 @@ public class SearchCoordinatorSvcImplTest {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
SlowIterator iter = new SlowIterator(pids.iterator(), 2);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(iter);
@ -294,32 +294,43 @@ public class SearchCoordinatorSvcImplTest {
}
@Test
public void testCancelActiveSearches() {
public void testCancelActiveSearches() throws InterruptedException {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 400);
SlowIterator iter = new SlowIterator(pids.iterator(), 50);
List<Long> pids = createPidSequence(800);
SlowIterator iter = new SlowIterator(pids.iterator(), 500);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null);
assertNotNull(result.getUuid());
assertEquals(null, result.size());
List<IBaseResource> resources;
resources = result.getResources(0, 1);
CountDownLatch completionLatch = new CountDownLatch(1);
Runnable taskStarter = () -> {
try {
ourLog.info("About to pull the first resource");
List<IBaseResource> resources = result.getResources(0, 1);
ourLog.info("Done pulling the first resource");
assertEquals(1, resources.size());
} finally {
completionLatch.countDown();
}
};
new Thread(taskStarter).start();
await().until(()->iter.getCountReturned() >= 3);
ourLog.info("About to cancel all searches");
mySvc.cancelAllActiveSearches();
ourLog.info("Done cancelling all searches");
try {
result.getResources(10, 20);
} catch (InternalErrorException e) {
assertEquals("Abort has been requested", e.getMessage());
}
completionLatch.await(10, TimeUnit.SECONDS);
}
/**
@ -331,7 +342,7 @@ public class SearchCoordinatorSvcImplTest {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
IResultIterator iter = new SlowIterator(pids.iterator(), 2);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(iter);
when(mySearchCacheSvc.save(any())).thenAnswer(t -> t.getArguments()[0]);
@ -376,7 +387,7 @@ public class SearchCoordinatorSvcImplTest {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 100);
List<Long> pids = createPidSequence(100);
SlowIterator iter = new SlowIterator(pids.iterator(), 2);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(iter);
@ -384,7 +395,7 @@ public class SearchCoordinatorSvcImplTest {
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null);
assertNotNull(result.getUuid());
assertEquals(90, result.size().intValue());
assertEquals(90, Objects.requireNonNull(result.size()).intValue());
List<IBaseResource> resources = result.getResources(0, 30);
assertEquals(30, resources.size());
@ -396,6 +407,7 @@ public class SearchCoordinatorSvcImplTest {
@Test
public void testGetPage() {
Pageable page = SearchCoordinatorSvcImpl.toPage(50, 73);
assert page != null;
assertEquals(50, page.getOffset());
assertEquals(23, page.getPageSize());
}
@ -458,14 +470,14 @@ public class SearchCoordinatorSvcImplTest {
params.setLoadSynchronous(true);
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
when(mySearchBuilder.createQuery(same(params), any(), any())).thenReturn(new ResultIterator(pids.iterator()));
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null);
assertNull(result.getUuid());
assertEquals(790, result.size().intValue());
assertEquals(790, Objects.requireNonNull(result.size()).intValue());
List<IBaseResource> resources = result.getResources(0, 10000);
assertEquals(790, resources.size());
@ -479,15 +491,15 @@ public class SearchCoordinatorSvcImplTest {
params.setLoadSynchronousUpTo(100);
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
List<Long> pids = createPidSequence(800);
when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class))).thenReturn(new ResultIterator(pids.iterator()));
pids = createPidSequence(10, 110);
pids = createPidSequence(110);
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(eq(pids), any(Collection.class), any(List.class), anyBoolean(), nullable(RequestDetails.class));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null);
assertNull(result.getUuid());
assertEquals(100, result.size().intValue());
assertEquals(100, Objects.requireNonNull(result.size()).intValue());
List<IBaseResource> resources = result.getResources(0, 10000);
assertEquals(100, resources.size());
@ -628,6 +640,7 @@ public class SearchCoordinatorSvcImplTest {
private int myDelay;
private Iterator<Long> myWrap;
private List<Long> myReturnedValues = new ArrayList<>();
private AtomicInteger myCountReturned = new AtomicInteger(0);
SlowIterator(Iterator<Long> theWrap, int theDelay) {
myWrap = theWrap;
@ -648,6 +661,10 @@ public class SearchCoordinatorSvcImplTest {
return retVal;
}
public int getCountReturned() {
return myCountReturned.get();
}
@Override
public Long next() {
try {
@ -657,6 +674,7 @@ public class SearchCoordinatorSvcImplTest {
}
Long retVal = myWrap.next();
myReturnedValues.add(retVal);
myCountReturned.incrementAndGet();
return retVal;
}

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.test.utilities.server;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.rules.TestRule;
@ -14,7 +13,6 @@ public class HashMapResourceProviderRule<T extends IBaseResource> extends HashMa
/**
* Constructor
*
* @param theFhirContext The FHIR context
* @param theResourceType The resource type to support
*/
public HashMapResourceProviderRule(RestfulServerRule theRestfulServerRule, Class<T> theResourceType) {

View File

@ -43,8 +43,8 @@
<ul class="dropdown-menu" role="menu">
<li th:each="serverEntry : ${serverEntries}">
<a th:href="'javascript:selectServer(\'' + ${serverEntry.key} + '\');'">
<span class="fa fa-check-square-o" th:if="${serverEntry.key} == ${serverId}"></span>
<span class="fa fa-square-o" style="color: #CCC;" th:unless="${serverEntry.key} == ${serverId}"></span>
<i class="far fa-check-square" th:if="${serverEntry.key} == ${serverId}"></i>
<i class="far fa-square" style="color: #CCC;" th:unless="${serverEntry.key} == ${serverId}"></i>
&nbsp;&nbsp;
<th:block th:text="${serverEntry.value}"/>
</a>

View File

@ -20,10 +20,10 @@ function addSearchParamRow() {
addSearchParamRow();
});
var params = new Array();
var params = [];
conformance.rest.forEach(function(rest){
rest.resource.forEach(function(restResource){
if (restResource.type == resourceName) {
if (restResource.type === resourceName) {
if (restResource.searchParam) {
for (var i = 0; i < restResource.searchParam.length; i++) {
var searchParam = restResource.searchParam[i];
@ -63,7 +63,7 @@ function updateSearchDateQualifier(qualifierBtn, qualifierInput, qualifier) {
function addSearchControls(theConformance, theSearchParamType, theSearchParamName, theSearchParamChain, theSearchParamTarget, theContainerRowNum, theRowNum) {
var addNameAndType = true;
if (theSearchParamType == 'id') {
if (theSearchParamType === 'id') {
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: 'id', type: 'text', 'class': 'form-control' })
@ -71,37 +71,37 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
);
} else if (theSearchParamType == 'token') {
var tokenQualifiers = new Array();
tokenQualifiers.push(new Object());
var tokenQualifiers = [];
tokenQualifiers.push({});
tokenQualifiers[0].name='Matches';
tokenQualifiers[0].value='';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[1].name='Text';
tokenQualifiers[1].value=':text';
tokenQualifiers[1].description='The search parameter is processed as a string that searches text associated with the code/value.';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[2].name='Not';
tokenQualifiers[2].value=':not';
tokenQualifiers[2].description='Reverse the code matching described in the paragraph above. Note that this includes resources that have no value for the parameter.';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[3].name='Above';
tokenQualifiers[3].value=':above';
tokenQualifiers[3].description='The search parameter is a concept with the form [system]|[code], and the search parameter tests whether the coding in a resource subsumes the specified search code. For example, the search concept has an is-a relationship with the coding in the resource, and this includes the coding itself.';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[4].name='Below';
tokenQualifiers[4].value=':below';
tokenQualifiers[4].description='The search parameter is a concept with the form [system]|[code], and the search parameter tests whether the coding in a resource is subsumed by the specified search code. For example, the coding in the resource has an is-a relationship with the search concept, and this includes the coding itself.';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[5].name='In';
tokenQualifiers[5].value=':in';
tokenQualifiers[5].description='The search parameter is a URI (relative or absolute) that identifies a value set, and the search parameter tests whether the coding is in the specified value set. The reference may be literal (to an address where the value set can be found) or logical (a reference to ValueSet.url). If the server can treat the reference as a literal URL, it does, else it tries to match known logical ValueSet.url values.';
tokenQualifiers.push(new Object());
tokenQualifiers.push({});
tokenQualifiers[6].name='Not-in';
tokenQualifiers[6].value=':not-in';
tokenQualifiers[6].description='The search parameter is a URI (relative or absolute) that identifies a value set, and the search parameter tests whether the coding is not in the specified value set.';
@ -117,8 +117,8 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
tokenQualifierInput.val(value);
tokenQualifierLabel.text(name);
}
};
var tokenQualifierLabel = $('<span>' + tokenQualifiers[0].name + '</span>');
}
var tokenQualifierLabel = $('<span>' + tokenQualifiers[0].name + '</span>');
var tokenQualifierDropdown = $('<ul />', {'class':'dropdown-menu', role:'menu'});
for (var i = 0; i < tokenQualifiers.length; i++) {
var qualName = tokenQualifiers[i].name;
@ -152,13 +152,13 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
)
);
} else if (theSearchParamType == 'string') {
} else if (theSearchParamType === 'string') {
var placeholderText = 'value';
var qualifiers = new Array();
qualifiers.push(new Object());
var qualifiers = [];
qualifiers.push({});
qualifiers[0].name='Matches';
qualifiers[0].value='';
qualifiers.push(new Object());
qualifiers.push({});
qualifiers[1].name='Exactly';
qualifiers[1].value=':exact';
@ -166,7 +166,6 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
$('#search-param-rowopts-' + theContainerRowNum).append(
qualifierInput
);
var matchesLabel = $('<span>' + qualifiers[0].name + '</span>');
var qualifierDropdown = $('<ul />', {'class':'dropdown-menu', role:'menu'});
@ -176,9 +175,8 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
qualifierInput.val(value);
matchesLabel.text(name);
}
};
for (var i = 0; i < qualifiers.length; i++) {
}
for (var i = 0; i < qualifiers.length; i++) {
var nextLink = $('<a>' + qualifiers[i].name+'</a>');
var qualName = qualifiers[i].name;
var nextValue = qualifiers[i].value;
@ -241,7 +239,7 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
var select = $('<select/>', {/*style:'margin-left:30px;'*/});
var newContainerRowNum = theContainerRowNum + "-0";
var newContainer = $('<div />', { id: 'search-param-rowopts-' + newContainerRowNum })
var newContainer = $('<div />', { id: 'search-param-rowopts-' + newContainerRowNum });
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<br clear="all" />'),
@ -255,14 +253,14 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
newContainer
);
var params = new Array();
var params = [];
{
var param = new Object();
var param = {};
param.type = 'id';
param.chain = '';
param.name = theSearchParamName;
param.documentation = 'The resource identity';
param.target = new Array();
param.target = [];
params[theSearchParamName] = param;
select.append(
$('<option />', { value: theSearchParamName }).text(param.name + ' - ' + param.documentation)
@ -294,13 +292,69 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
}
}
}
select.select2();
handleSearchParamTypeChange(select, params, newContainerRowNum, theRowNum);
select.change(function(){ handleSearchParamTypeChange(select, params, newContainerRowNum, theRowNum); });
addNameAndType = false;
}
} else if (theSearchParamType == 'uri') {
var placeholderText = 'value';
var qualifiers = [];
qualifiers.push({});
qualifiers[0].name = 'Equals';
qualifiers[0].value = '';
qualifiers.push({});
qualifiers[1].name = 'Above';
qualifiers[1].value = ':above';
qualifiers.push({});
qualifiers[2].name = 'Below';
qualifiers[2].value = ':below';
var qualifierInput = $('<input />', {id: 'param.' + theRowNum + '.qualifier', type: 'hidden'});
$('#search-param-rowopts-' + theContainerRowNum).append(
qualifierInput
);
var matchesLabel = $('<span>' + qualifiers[0].name + '</span>');
var qualifierDropdown = $('<ul />', {'class': 'dropdown-menu', role: 'menu'});
function clickFunction(value, name) {
return function () {
qualifierInput.val(value);
matchesLabel.text(name);
}
}
for (var i = 0; i < qualifiers.length; i++) {
var nextLink = $('<a>' + qualifiers[i].name + '</a>');
var qualName = qualifiers[i].name;
var nextValue = qualifiers[i].value;
qualifierDropdown.append($('<li />').append(nextLink));
nextLink.click(clickFunction(nextValue, qualName));
}
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', {'class': 'col-sm-5 input-group'}).append(
$('<div />', {'class': 'input-group-btn'}).append(
$('<button />', {'class': 'btn btn-default dropdown-toggle', 'data-toggle': 'dropdown'}).append(
matchesLabel,
$('<span class="caret" style="margin-left: 5px;"></span>')
),
qualifierDropdown
),
$('<input />', {
id: 'param.' + theRowNum + '.0',
placeholder: placeholderText,
type: 'text',
'class': 'form-control'
})
)
);
addNameAndType = true;
}
if (addNameAndType) {
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<input />', { id: 'param.' + theRowNum + '.name', type: 'hidden', value: theSearchParamName }),
@ -314,12 +368,13 @@ function addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum,
var inputId0 = theRowNum + '.' + (theLower ? 0 : 2);
var inputId1 = theRowNum + '.' + (theLower ? 1 : 3);
var qualifier = $('<input />', {type:'hidden', id:'param.'+inputId0, id:'param.'+inputId0});
var qualifier = $('<input />', {type:'hidden', id:'param.'+inputId0});
var input;
if (/date$/.test(theSearchParamName)) {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' });
input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' });
} else {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' });
input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' });
}
var qualifierDiv = $('<div />');
@ -426,7 +481,7 @@ function addSearchControlQuantity(theSearchParamName, theContainerRowNum, theRow
function handleSearchParamTypeChange(select, params, theContainerRowNum, theParamRowNum) {
var oldVal = select.prevVal;
var newVal = select.val();
if (oldVal == newVal) {
if (oldVal === newVal) {
return;
}
$('#search-param-rowopts-' + theContainerRowNum).empty();
@ -448,10 +503,9 @@ function handleSearchParamTypeChange(select, params, theContainerRowNum, thePara
function readFromEntriesTable(source, type, id, vid) {
var btn = $(source);
btn.button('loading');
var resId = source.resourceid;
btn.append($('<input />', { type: 'hidden', name: 'id', value: id }));
var resVid = source.resourcevid;
if (resVid != '') {
if (resVid !== '') {
btn.append($('<input />', { type: 'hidden', name: 'vid', value: vid }));
}
setResource(btn, type);
@ -465,10 +519,9 @@ function readFromEntriesTable(source, type, id, vid) {
function updateFromEntriesTable(source, type, id, vid) {
var btn = $(source);
btn.button('loading');
var resId = source.resourceid;
btn.append($('<input />', { type: 'hidden', name: 'updateId', value: id }));
var resVid = source.resourcevid;
if (resVid != '') {
if (resVid !== '') {
btn.append($('<input />', { type: 'hidden', name: 'updateVid', value: vid }));
}
setResource(btn, type);
@ -488,7 +541,7 @@ function updateURLParameter(url, param, paramVal){
if (additionalURL) {
tempArray = additionalURL.split("&");
for (i=0; i<tempArray.length; i++){
if(tempArray[i].split('=')[0] != param){
if(tempArray[i].split('=')[0] !== param){
newAdditionalURL += temp + tempArray[i];
temp = "&";
}
@ -501,8 +554,9 @@ function updateURLParameter(url, param, paramVal){
function selectServer(serverId) {
$('#serverSelectorFhirIcon').removeClass();
$('#serverSelectorFhirIcon').addClass('fa fa-spinner fa-spin');
let $serverSelectorFhirIcon = $('#serverSelectorFhirIcon');
$serverSelectorFhirIcon.removeClass();
$serverSelectorFhirIcon.addClass('fa fa-spinner fa-spin');
$('#serverSelectorName').text("Loading...");
$('#serverId').val(serverId);
$("#outerForm").attr("action", "home").submit();
@ -511,7 +565,6 @@ function selectServer(serverId) {
function setResource(target, resourceName) {
var resource = $('#resource');
if (resourceName != null) {
var input = $('#resource');
if (resource.length) {
resource.val(resourceName);
} else {
@ -527,7 +580,7 @@ function setResource(target, resourceName) {
function updateSort(value) {
$('#sort_by').val(value);
if (value == '') {
if (value === '') {
$('#search_sort_button').text('Default Sort');
} else {
$('#search_sort_button').text(value);
@ -536,7 +589,7 @@ function updateSort(value) {
function updateSortDirection(value) {
$('#sort_direction').val(value);
if (value == '') {
if (value === '') {
$('#search_sort_direction_button').text('Default');
} else {
$('#search_sort_direction_button').text(value);

View File

@ -473,6 +473,9 @@
The @ProvidesResources annotation has been removed from HAPI FHIR, as it was not documented
and did not do anything useful. Please get in touch if this causes any issues.
</action>
<action type="fix">
Search parameters of type URI did not work in the hapi-fhir-testpage-overlay. This has been corrected.
</action>
</release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">