Fix #227 - JPA server should reject resources with a reference that
points to an incorrectly typed resource (e.g. points to Patient/123 but resource 123 is actually an Observation) or points to a resource that is not valid in the location it is found in (e.g. points to Patient/123 but the field supposed to reference an Organization)
This commit is contained in:
parent
072c1ece87
commit
4ff7452c9b
|
@ -65,9 +65,11 @@ import com.google.common.collect.ArrayListMultimap;
|
|||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
|
@ -171,6 +173,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
|
||||
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
|
||||
continue;
|
||||
}
|
||||
|
@ -187,6 +191,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
String[] nextPathsSplit = nextPathsUnsplit.split("\\|");
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
List<Class<? extends IBaseResource>> allowedTypesInField = null;
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
|
@ -240,10 +245,43 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
|
||||
}
|
||||
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
|
||||
RuntimeResourceDefinition targetResourceDef = getContext().getResourceDefinition(type);
|
||||
if (target == null) {
|
||||
String resName = getContext().getResourceDefinition(type).getName();
|
||||
String resName = targetResourceDef.getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
|
||||
}
|
||||
|
||||
if (!typeString.equals(target.getResourceType())) {
|
||||
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart() + " is actually of type " + target.getResourceType());
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the target type an allowable type of resource for the path where it is referenced?
|
||||
*/
|
||||
|
||||
if (allowedTypesInField == null) {
|
||||
BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPath);
|
||||
if (childDef instanceof RuntimeChildResourceDefinition) {
|
||||
RuntimeChildResourceDefinition resRefDef = (RuntimeChildResourceDefinition) childDef;
|
||||
allowedTypesInField = resRefDef.getResourceTypes();
|
||||
} else {
|
||||
allowedTypesInField = new ArrayList<Class<? extends IBaseResource>>();
|
||||
allowedTypesInField.add(IBaseResource.class);
|
||||
}
|
||||
}
|
||||
|
||||
boolean acceptableLink = false;
|
||||
for(Class<? extends IBaseResource> next : allowedTypesInField) {
|
||||
if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) {
|
||||
acceptableLink = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!acceptableLink) {
|
||||
throw new UnprocessableEntityException("Invalid reference found at path '" + nextPath + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path");
|
||||
}
|
||||
|
||||
nextEntity = new ResourceLink(nextPath, theEntity, target);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
|
|
|
@ -163,6 +163,71 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithInvalid() {
|
||||
Observation o1 = new Observation();
|
||||
o1.getCode().addCoding().setSystem("foo").setCode("testChoiceParam01");
|
||||
o1.setValue(new CodeableConceptDt("testChoiceParam01CCS", "testChoiceParam01CCV"));
|
||||
IIdType id1 = myObservationDao.create(o1).getId();
|
||||
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV"));
|
||||
assertEquals(1, found.size());
|
||||
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithIllegalReference() {
|
||||
Observation o1 = new Observation();
|
||||
o1.getCode().addCoding().setSystem("foo").setCode("testChoiceParam01");
|
||||
IIdType id1 = myObservationDao.create(o1).getId().toUnqualifiedVersionless();
|
||||
|
||||
try {
|
||||
Patient p = new Patient();
|
||||
p.getManagingOrganization().setReference(id1);
|
||||
myPatientDao.create(p);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Invalid reference found at path 'Patient.managingOrganization'. Resource type 'Observation' is not valid for this path", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Patient p = new Patient();
|
||||
p.getManagingOrganization().setReference(new IdDt("Organization", id1.getIdPart()));
|
||||
myPatientDao.create(p);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Resource contains reference to Organization/1 but resource with ID 1 is actually of type Observation", e.getMessage());
|
||||
}
|
||||
|
||||
// Now with a forced ID
|
||||
|
||||
o1 = new Observation();
|
||||
o1.setId("testCreateWithIllegalReference");
|
||||
o1.getCode().addCoding().setSystem("foo").setCode("testChoiceParam01");
|
||||
id1 = myObservationDao.update(o1).getId().toUnqualifiedVersionless();
|
||||
|
||||
try {
|
||||
Patient p = new Patient();
|
||||
p.getManagingOrganization().setReference(id1);
|
||||
myPatientDao.create(p);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Invalid reference found at path 'Patient.managingOrganization'. Resource type 'Observation' is not valid for this path", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Patient p = new Patient();
|
||||
p.getManagingOrganization().setReference(new IdDt("Organization", id1.getIdPart()));
|
||||
myPatientDao.create(p);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Resource contains reference to Organization/testCreateWithIllegalReference but resource with ID testCreateWithIllegalReference is actually of type Observation", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoiceParamDate() {
|
||||
Observation o2 = new Observation();
|
||||
|
|
|
@ -60,6 +60,12 @@
|
|||
QuestionnaireResponse validator now allows responses to questions of
|
||||
type OPENCHOICE to be of type 'string'
|
||||
</action>
|
||||
<action type="fix" issue="227">
|
||||
JPA server should reject resources with a reference that points to an incorrectly typed
|
||||
resource (e.g. points to Patient/123 but resource 123 is actually an Observation) or points
|
||||
to a resource that is not valid in the location it is found in (e.g. points to Patient/123 but
|
||||
the field supposed to reference an Organization). Thanks to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue