package ca.uhn.fhir.jpa.dao.dstu2;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.Arrays;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2ValidateTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
myDaoConfig.setAllowExternalReferences(true);
}
@After
public void after() {
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
}
@Test
public void testValidateResourceContainingProfileDeclarationJson() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationJson";
OperationOutcome outcome = doTestValidateResourceContainingProfileDeclaration(methodName, EncodingEnum.JSON);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Element '.subject': minimum required = 1, but only found 0"));
assertThat(ooString, containsString("Element encounter @ : max allowed = 0, but found 1"));
assertThat(ooString, containsString("Element '.device': minimum required = 1, but only found 0"));
}
@Test
public void testValidateResourceContainingProfileDeclarationXml() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationXml";
OperationOutcome outcome = doTestValidateResourceContainingProfileDeclaration(methodName, EncodingEnum.XML);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Element '/f:Observation.subject': minimum required = 1, but only found 0"));
assertThat(ooString, containsString("Element encounter @ /f:Observation: max allowed = 0, but found 1"));
assertThat(ooString, containsString("Element '/f:Observation.device': minimum required = 1, but only found 0"));
}
private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException {
Bundle vss = loadResourceFromClasspath(Bundle.class, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-status"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-category"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-codes"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-methods"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-valueabsentreason"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-interpretation"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "body-site"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "referencerange-meaning"), mySrd);
myValueSetDao.update((ValueSet) findResourceByIdInBundle(vss, "observation-relationshiptypes"), mySrd);
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/org/hl7/fhir/instance/model/profile/devicemetricobservation.profile.xml");
sd.setId(new IdDt());
sd.setUrl("http://example.com/foo/bar/" + methodName);
myStructureDefinitionDao.create(sd, mySrd);
Observation input = new Observation();
ResourceMetadataKeyEnum.PROFILES.put(input, Arrays.asList(new IdDt(sd.getUrl())));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getEncounter().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatusEnum.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
String encoded = null;
MethodOutcome outcome = null;
ValidationModeEnum mode = ValidationModeEnum.CREATE;
switch (enc) {
case JSON:
encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
try {
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
fail();
} catch (PreconditionFailedException e) {
return (OperationOutcome) e.getOperationOutcome();
}
case XML:
encoded = myFhirCtx.newXmlParser().encodeResourceToString(input);
try {
myObservationDao.validate(input, null, encoded, EncodingEnum.XML, mode, null, mySrd);
fail();
} catch (PreconditionFailedException e) {
return (OperationOutcome) e.getOperationOutcome();
}
}
throw new IllegalStateException(); // shouldn't get here
}
@Test
public void testValidateResourceContainingProfileDeclarationInvalid() throws Exception {
String methodName = "testValidateResourceContainingProfileDeclarationInvalid";
Observation input = new Observation();
String profileUri = "http://example.com/" + methodName;
ResourceMetadataKeyEnum.PROFILES.put(input, Arrays.asList(new IdDt(profileUri)));
input.addIdentifier().setSystem("http://acme").setValue("12345");
input.getEncounter().setReference("http://foo.com/Encounter/9");
input.setStatus(ObservationStatusEnum.FINAL);
input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345");
ValidationModeEnum mode = ValidationModeEnum.CREATE;
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
MethodOutcome outcome = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
ourLog.info(ooString);
assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved"));
}
@Test
public void testValidateForCreate() {
String methodName = "testValidateForCreate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must not be populated"));
}
pat.setId("");
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
}
@Test
public void testValidateForUpdate() {
String methodName = "testValidateForUpdate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForUpdateWithContained() {
String methodName = "testValidateForUpdate";
Organization org = new Organization();
org.setId("#123");
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForDelete() {
String methodName = "testValidateForDelete";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
Patient pat = new Patient();
pat.addName().addFamily(methodName);
pat.getManagingOrganization().setReference(orgId);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
OperationOutcome outcome = null;
try {
myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null, mySrd);
fail();
} catch (ResourceVersionConflictException e) {
outcome = (OperationOutcome) e.getOperationOutcome();
}
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Unable to delete Organization"));
pat.setId(patId);
pat.getManagingOrganization().setReference("");
myPatientDao.update(pat, mySrd);
outcome = (OperationOutcome) myOrganizationDao.validate(null, orgId, null, null, ValidationModeEnum.DELETE, null, mySrd).getOperationOutcome();
ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(ooString);
assertThat(ooString, containsString("Ok to delete"));
}
private IResource findResourceByIdInBundle(Bundle vss, String name) {
IResource retVal = null;
for (Entry next : vss.getEntry()) {
if (next.getResource().getId().getIdPart().equals(name)) {
retVal = next.getResource();
break;
}
}
if (retVal == null) {
fail("Can't find VS: " + name);
}
return retVal;
}
}