Correctly handle JPA search by token with system but no code
This commit is contained in:
parent
d69365988f
commit
f9fa6265df
|
@ -35,17 +35,37 @@ public class TokenClientParam extends BaseClientParam implements IParam {
|
|||
|
||||
private String myParamName;
|
||||
|
||||
@Override
|
||||
public String getParamName() {
|
||||
return myParamName;
|
||||
}
|
||||
|
||||
public TokenClientParam(String theParamName) {
|
||||
myParamName = theParamName;
|
||||
}
|
||||
|
||||
public IMatches exactly() {
|
||||
return new IMatches() {
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> code(String theCode) {
|
||||
return new TokenCriterion(getParamName(), null, theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifier(BaseIdentifierDt theIdentifier) {
|
||||
return new TokenCriterion(getParamName(), theIdentifier.getSystemElement().getValueAsString(), theIdentifier.getValueElement().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifier(String theIdentifier) {
|
||||
return new TokenCriterion(getParamName(), null, theIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifiers(BaseIdentifierDt... theIdentifiers) {
|
||||
return new TokenCriterion(getParamName(), Arrays.asList(theIdentifiers));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifiers(List<BaseIdentifierDt> theIdentifiers) {
|
||||
return new TokenCriterion(getParamName(), theIdentifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> systemAndCode(String theSystem, String theCode) {
|
||||
return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
|
||||
|
@ -55,35 +75,74 @@ public class TokenClientParam extends BaseClientParam implements IParam {
|
|||
public ICriterion<TokenClientParam> systemAndIdentifier(String theSystem, String theCode) {
|
||||
return new TokenCriterion(getParamName(), defaultString(theSystem), theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> code(String theCode) {
|
||||
return new TokenCriterion(getParamName(), null, theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifier(String theIdentifier) {
|
||||
return new TokenCriterion(getParamName(), null, theIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifier(BaseIdentifierDt theIdentifier) {
|
||||
return new TokenCriterion(getParamName(), theIdentifier.getSystemElement().getValueAsString(), theIdentifier.getValueElement().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifiers(List<BaseIdentifierDt> theIdentifiers) {
|
||||
return new TokenCriterion(getParamName(), theIdentifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion<TokenClientParam> identifiers(BaseIdentifierDt... theIdentifiers) {
|
||||
return new TokenCriterion(getParamName(), Arrays.asList(theIdentifiers));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParamName() {
|
||||
return myParamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a search criterion that matches against the given system
|
||||
* value but does not specify a code. This means that any code/identifier with
|
||||
* the given system should match.
|
||||
* <p>
|
||||
* Use {@link #exactly()} if you want to specify a code.
|
||||
* </p>
|
||||
*/
|
||||
public ICriterion<TokenClientParam> hasSystemWithAnyCode(String theSystem) {
|
||||
return new TokenCriterion(getParamName(), theSystem, null);
|
||||
}
|
||||
|
||||
public interface IMatches {
|
||||
/**
|
||||
* Creates a search criterion that matches against the given code, with no code system specified
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> code(String theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given identifier (system and code if both are present, or whatever is present)
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifier(BaseIdentifierDt theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given identifier, with no system specified
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifier(String theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given collection of identifiers (system and code if both are present, or whatever is present).
|
||||
* In the query URL that is generated, identifiers will be joined with a ',' to create an OR query.
|
||||
*
|
||||
* @param theIdentifiers
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifiers(BaseIdentifierDt... theIdentifiers);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given collection of identifiers (system and code if both are present, or whatever is present).
|
||||
* In the query URL that is generated, identifiers will be joined with a ',' to create an OR query.
|
||||
*
|
||||
* @param theIdentifiers
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifiers(List<BaseIdentifierDt> theIdentifiers);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given code system and code
|
||||
*
|
||||
|
@ -106,53 +165,6 @@ public class TokenClientParam extends BaseClientParam implements IParam {
|
|||
*/
|
||||
ICriterion<TokenClientParam> systemAndIdentifier(String theSystem, String theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given identifier, with no system specified
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifier(String theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given code, with no code system specified
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> code(String theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given identifier (system and code if both are present, or whatever is present)
|
||||
*
|
||||
* @param theIdentifier
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifier(BaseIdentifierDt theIdentifier);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given collection of identifiers (system and code if both are present, or whatever is present).
|
||||
* In the query URL that is generated, identifiers will be joined with a ',' to create an OR query.
|
||||
*
|
||||
* @param theIdentifiers
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifiers(List<BaseIdentifierDt> theIdentifiers);
|
||||
|
||||
/**
|
||||
* Creates a search criterion that matches against the given collection of identifiers (system and code if both are present, or whatever is present).
|
||||
* In the query URL that is generated, identifiers will be joined with a ',' to create an OR query.
|
||||
*
|
||||
* @param theIdentifiers
|
||||
* The identifier
|
||||
* @return A criterion
|
||||
*/
|
||||
ICriterion<TokenClientParam> identifiers(BaseIdentifierDt... theIdentifiers);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1164,7 +1164,14 @@ public class SearchBuilder {
|
|||
if (StringUtils.isNotBlank(code)) {
|
||||
singleCodePredicates.add(theBuilder.equal(theFrom.get("myValue"), code));
|
||||
} else {
|
||||
singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
|
||||
/*
|
||||
* As of HAPI FHIR 1.5, if the client searched for a token with a system but
|
||||
* no specified value this means to match all tokens with the given value.
|
||||
*
|
||||
* I'm not sure I agree with this, but hey.. FHIR-I voted and this was
|
||||
* the result :)
|
||||
*/
|
||||
//singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
|
||||
}
|
||||
Predicate singleCode = theBuilder.and(toArray(singleCodePredicates));
|
||||
return singleCode;
|
||||
|
|
|
@ -1373,6 +1373,38 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTokenParamNoValue() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
|
||||
patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem").setDisplay("testSearchTokenParamDisplay");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam002");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam002");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
{
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", null));
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
assertEquals(2, retrieved.size());
|
||||
}
|
||||
{
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", ""));
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
assertEquals(2, retrieved.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSearchUnknownContentParam() {
|
||||
|
|
|
@ -88,6 +88,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
|
@ -97,7 +98,9 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
|||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
|
@ -116,6 +119,35 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTokenParamNoValue() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
|
||||
patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem").setDisplay("testSearchTokenParamDisplay");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam002");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam002");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
//@formatter:off
|
||||
Bundle response = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.IDENTIFIER.hasSystemWithAnyCode("urn:system"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals(2, response.getEntry().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateConditional() {
|
||||
|
|
|
@ -731,17 +731,24 @@ public class GenericClientTest {
|
|||
@Test
|
||||
public void testSearchByDate() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
final String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
@SuppressWarnings("deprecation")
|
||||
Bundle response = client.search()
|
||||
.forResource(Patient.class)
|
||||
.encodedJson()
|
||||
|
@ -755,7 +762,23 @@ public class GenericClientTest {
|
|||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_sort=address&_count=123&_format=json", capt.getValue().getURI().toString());
|
||||
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_sort=address&_count=123&_format=json", capt.getAllValues().get(idx++).getURI().toString());
|
||||
|
||||
//@formatter:off
|
||||
response = client.search()
|
||||
.forResource(Patient.class)
|
||||
.encodedJson()
|
||||
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
|
||||
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
|
||||
.include(Patient.INCLUDE_MANAGINGORGANIZATION)
|
||||
.sort().ascending(Patient.BIRTHDATE)
|
||||
.sort().descending(Patient.NAME)
|
||||
.sort().defaultOrder(Patient.ADDRESS)
|
||||
.count(123)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_sort=address&_count=123&_format=json", capt.getAllValues().get(idx++).getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1043,6 +1066,52 @@ public class GenericClientTest {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testSearchByTokenWithSystemAndNoCode() throws Exception {
|
||||
|
||||
final String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
Bundle response = client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.IDENTIFIER.hasSystemWithAnyCode("urn:foo"))
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/Patient?identifier=urn%3Afoo%7C", capt.getAllValues().get(idx++).getURI().toString());
|
||||
//@formatter:on
|
||||
|
||||
//@formatter:off
|
||||
response = client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:foo", null))
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/Patient?identifier=urn%3Afoo%7C", capt.getAllValues().get(idx++).getURI().toString());
|
||||
//@formatter:on
|
||||
|
||||
//@formatter:off
|
||||
response = client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:foo", ""))
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/Patient?identifier=urn%3Afoo%7C", capt.getAllValues().get(idx++).getURI().toString());
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for #192
|
||||
*/
|
||||
|
|
|
@ -305,6 +305,15 @@
|
|||
sources. Thanks to Bill Denton for the pull
|
||||
request!
|
||||
</actiom>
|
||||
<action type="fix">
|
||||
JPA server now allows searching by token
|
||||
parameter using a system only and no code,
|
||||
giving a search for any tokens which match
|
||||
the given token with any code. Previously the
|
||||
expected behaviour for this search
|
||||
was not clear in the spec and HAPI had different
|
||||
behaviour from the other reference servers.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue