Better error message for unqualified search parameter types
This commit is contained in:
parent
82f40f0423
commit
c484c69664
|
@ -49,9 +49,8 @@ public class RuntimeChildResourceDefinition extends BaseRuntimeDeclaredChildDefi
|
|||
myResourceTypes = theResourceTypes;
|
||||
|
||||
if (theResourceTypes == null || theResourceTypes.isEmpty()) {
|
||||
myResourceTypes = new ArrayList<Class<? extends IBaseResource>>();
|
||||
myResourceTypes = new ArrayList<>();
|
||||
myResourceTypes.add(IBaseResource.class);
|
||||
// throw new ConfigurationException("Field '" + theField.getName() + "' on type '" + theField.getDeclaringClass().getCanonicalName() + "' has no resource types noted");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -39,16 +41,42 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
|
|||
return myName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a chained search. For example:
|
||||
* <pre>
|
||||
* Bundle resp = ourClient
|
||||
* .search()
|
||||
* .forResource(QuestionnaireResponse.class)
|
||||
* .where(QuestionnaireResponse.SUBJECT.hasChainedProperty(Patient.FAMILY.matches().value("SMITH")))
|
||||
* .returnBundle(Bundle.class)
|
||||
* .execute();
|
||||
* </pre>
|
||||
*/
|
||||
public ICriterion<ReferenceClientParam> hasChainedProperty(ICriterion<?> theCriterion) {
|
||||
return new ReferenceChainCriterion(getParamName(), theCriterion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a chained search with a resource type. For example:
|
||||
* <pre>
|
||||
* Bundle resp = ourClient
|
||||
* .search()
|
||||
* .forResource(QuestionnaireResponse.class)
|
||||
* .where(QuestionnaireResponse.SUBJECT.hasChainedProperty("Patient", Patient.FAMILY.matches().value("SMITH")))
|
||||
* .returnBundle(Bundle.class)
|
||||
* .execute();
|
||||
* </pre>
|
||||
*/
|
||||
public ICriterion<ReferenceClientParam> hasChainedProperty(String theResourceType, ICriterion<?> theCriterion) {
|
||||
return new ReferenceChainCriterion(getParamName(), theResourceType, theCriterion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the referenced resource if the resource has the given ID (this can be
|
||||
* the logical ID or the absolute URL of the resource)
|
||||
*/
|
||||
public ICriterion<ReferenceClientParam> hasId(IdDt theId) {
|
||||
return new StringCriterion<ReferenceClientParam>(getParamName(), theId.getValue());
|
||||
public ICriterion<ReferenceClientParam> hasId(IIdType theId) {
|
||||
return new StringCriterion<>(getParamName(), theId.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,7 +84,7 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
|
|||
* the logical ID or the absolute URL of the resource)
|
||||
*/
|
||||
public ICriterion<ReferenceClientParam> hasId(String theId) {
|
||||
return new StringCriterion<ReferenceClientParam>(getParamName(), theId);
|
||||
return new StringCriterion<>(getParamName(), theId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,22 +95,28 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
|
|||
* with the same parameter.
|
||||
*/
|
||||
public ICriterion<ReferenceClientParam> hasAnyOfIds(Collection<String> theIds) {
|
||||
return new StringCriterion<ReferenceClientParam>(getParamName(), theIds);
|
||||
return new StringCriterion<>(getParamName(), theIds);
|
||||
}
|
||||
|
||||
private static class ReferenceChainCriterion implements ICriterion<ReferenceClientParam>, ICriterionInternal {
|
||||
|
||||
private final String myResourceTypeQualifier;
|
||||
private String myParamName;
|
||||
private ICriterionInternal myWrappedCriterion;
|
||||
|
||||
public ReferenceChainCriterion(String theParamName, ICriterion<?> theWrappedCriterion) {
|
||||
ReferenceChainCriterion(String theParamName, ICriterion<?> theWrappedCriterion) {
|
||||
this(theParamName, null, theWrappedCriterion);
|
||||
}
|
||||
|
||||
ReferenceChainCriterion(String theParamName, String theResourceType, ICriterion<?> theWrappedCriterion) {
|
||||
myParamName = theParamName;
|
||||
myResourceTypeQualifier = isNotBlank(theResourceType) ? ":" + theResourceType : "";
|
||||
myWrappedCriterion = (ICriterionInternal) theWrappedCriterion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameterName() {
|
||||
return myParamName + "." + myWrappedCriterion.getParameterName();
|
||||
return myParamName + myResourceTypeQualifier + "." + myWrappedCriterion.getParameterName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -462,6 +462,11 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
} else if (def instanceof RuntimeChildResourceDefinition) {
|
||||
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
|
||||
resourceTypes.addAll(resDef.getResourceTypes());
|
||||
if (resourceTypes.size() == 1) {
|
||||
if (resourceTypes.get(0).isInterface()) {
|
||||
throw new InvalidRequestException("Unable to perform search for unqualified chain '" + theParamName + "' as this SearchParameter does not declare any target types. Add a qualifier of the form '" + theParamName + ":[ResourceType]' to perform this search.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Property " + paramPath + " of type " + myResourceName + " is not a resource: " + def.getClass());
|
||||
}
|
||||
|
@ -479,10 +484,14 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
resourceId = ref.getValue();
|
||||
|
||||
} else {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(ref.getResourceType());
|
||||
resourceTypes = new ArrayList<>(1);
|
||||
resourceTypes.add(resDef.getImplementingClass());
|
||||
resourceId = ref.getIdPart();
|
||||
try {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(ref.getResourceType());
|
||||
resourceTypes = new ArrayList<>(1);
|
||||
resourceTypes.add(resDef.getImplementingClass());
|
||||
resourceId = ref.getIdPart();
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException("Invalid resource type: " + ref.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
boolean foundChainMatch = false;
|
||||
|
|
|
@ -75,11 +75,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3Test.class);
|
||||
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
|
@ -240,6 +235,59 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchChainedReference() {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("SMITH");
|
||||
IIdType pid = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
QuestionnaireResponse qr = new QuestionnaireResponse();
|
||||
qr.getSubject().setReference(pid.getValue());
|
||||
ourClient.create().resource(qr).execute();
|
||||
|
||||
Subscription subs = new Subscription();
|
||||
subs.setStatus(SubscriptionStatus.ACTIVE);
|
||||
subs.getChannel().setType(SubscriptionChannelType.WEBSOCKET);
|
||||
subs.setCriteria("Observation?");
|
||||
IIdType id = ourClient.create().resource(subs).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
// Unqualified (doesn't work because QuestionnaireRespone.subject is a Refercence(Any))
|
||||
try {
|
||||
ourClient
|
||||
.search()
|
||||
.forResource(QuestionnaireResponse.class)
|
||||
.where(QuestionnaireResponse.SUBJECT.hasChainedProperty(Patient.FAMILY.matches().value("SMITH")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Unable to perform search for unqualified chain 'subject' as this SearchParameter does not declare any target types. Add a qualifier of the form 'subject:[ResourceType]' to perform this search.", e.getMessage());
|
||||
}
|
||||
|
||||
// Qualified
|
||||
Bundle resp = ourClient
|
||||
.search()
|
||||
.forResource(QuestionnaireResponse.class)
|
||||
.where(QuestionnaireResponse.SUBJECT.hasChainedProperty("Patient", Patient.FAMILY.matches().value("SMITH")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
assertEquals(1, resp.getEntry().size());
|
||||
|
||||
// Qualified With an invalid name
|
||||
try {
|
||||
ourClient
|
||||
.search()
|
||||
.forResource(QuestionnaireResponse.class)
|
||||
.where(QuestionnaireResponse.SUBJECT.hasChainedProperty("FOO", Patient.FAMILY.matches().value("SMITH")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("HTTP 400 Bad Request: Invalid resource type: FOO", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCodeSearch() {
|
||||
Subscription subs = new Subscription();
|
||||
|
@ -1588,6 +1636,25 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertEquals(77, ids.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEverythingWithOnlyPatient() {
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(300 * 1000);
|
||||
|
||||
Bundle response = ourClient
|
||||
.operation()
|
||||
.onInstance(id)
|
||||
.named("everything")
|
||||
.withNoParameters(Parameters.class)
|
||||
.returnResourceType(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals(1, response.getEntry().size());
|
||||
}
|
||||
|
||||
// private void delete(String theResourceType, String theParamName, String theParamValue) {
|
||||
// Bundle resources;
|
||||
// do {
|
||||
|
@ -1613,25 +1680,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testEverythingWithOnlyPatient() {
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(300 * 1000);
|
||||
|
||||
Bundle response = ourClient
|
||||
.operation()
|
||||
.onInstance(id)
|
||||
.named("everything")
|
||||
.withNoParameters(Parameters.class)
|
||||
.returnResourceType(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals(1, response.getEntry().size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testFullTextSearch() throws Exception {
|
||||
|
@ -4293,4 +4341,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
return new InstantDt(theDate).getValueAsString();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -102,6 +102,16 @@
|
|||
Oracle or SQL Server. In addition, when using the "Dry Run" option, all generated SQL
|
||||
statements will be logged at the end of the run.
|
||||
</action>
|
||||
<action type="add">
|
||||
In the JPA server, when performing a chained reference search on a search parameter with
|
||||
a target type of
|
||||
<![CDATA[<code>Reference(Any)</code>]]>, the search failed with an incomprehensible
|
||||
error. This has been corrected to return an error message indicating that the chain
|
||||
must be qualified with a resource type for such a field. For example,
|
||||
<![CDATA[<code>QuestionnaireResponse?subject:Patient.name=smith</code>]]>
|
||||
instead of
|
||||
<![CDATA[<code>QuestionnaireResponse?subject.name=smith</code>]]>.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.6.0" date="2018-11-12" description="Food">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue