diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 7d1b0dcbfb1..909d8c4e6ce 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -145,9 +145,6 @@ public abstract class BaseParser implements IParser { /* * There are lots of reasons we might skip encoding a particular child */ - // if (myNext.getDef().getElementName().equals("extension") || myNext.getDef().getElementName().equals("modifierExtension")) { - // myNext = null; - // } else if (myNext.getDef().getElementName().equals("id")) { myNext = null; } else if (!myNext.shouldBeEncoded()) { @@ -981,6 +978,12 @@ public abstract class BaseParser implements IParser { } if (myDef != null) { + if (myDef.getMin() > 0) { + if (theElements.contains("*.(mandatory)")) { + return true; + } + } + thePathBuilder.append('.'); thePathBuilder.append(myDef.getElementName()); return theElements.contains(thePathBuilder.toString()); @@ -1016,6 +1019,12 @@ public abstract class BaseParser implements IParser { retVal = !checkIfParentShouldNotBeEncodedAndBuildPath(new StringBuilder(), true); } } +// if (retVal == false && myEncodeElements.contains("*.(mandatory)")) { +// if (myDef.getMin() > 0) { +// retVal = true; +// } +// } + return retVal; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java index 215497bb5e1..0627f5db84f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java @@ -257,6 +257,7 @@ public interface IParser { *
  • Patient.name.family - Encode only the patient's family name
  • *
  • *.text - Encode the text element on any resource (only the very first position may contain a * wildcard)
  • + *
  • *.(mandatory) - This is a special case which causes any mandatory fields (min > 0) to be encoded
  • * * * @param theEncodeElements diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 5cd377a204b..3089e68f60e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -71,7 +71,7 @@ public class RestfulServerUtils { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class); - private static final HashSet TEXT_ENCODE_ELEMENTS = new HashSet(Arrays.asList("Bundle", "*.text")); + private static final HashSet TEXT_ENCODE_ELEMENTS = new HashSet(Arrays.asList("Bundle", "*.text", "*.(mandatory)")); public static void configureResponseParser(RequestDetails theRequestDetails, IParser parser) { // Pretty print diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java index ae47d912b5b..6f25e2a4f46 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoBundleDstu2.java @@ -31,8 +31,8 @@ public class FhirResourceDaoBundleDstu2 extends FhirResourceDaoDstu2 { protected void preProcessResourceForStorage(Bundle theResource) { super.preProcessResourceForStorage(theResource); - if (theResource.getTypeElement().getValueAsEnum() != BundleTypeEnum.DOCUMENT) { - String message = "Unable to store a Bundle resource on this server with a Bundle.type value other than '" + BundleTypeEnum.DOCUMENT.getCode() + "' - Value was: " + (theResource.getTypeElement().getValueAsEnum() != null ? theResource.getTypeElement().getValueAsEnum().getCode() : "(missing)"); + if (theResource.getTypeElement().getValueAsEnum() != BundleTypeEnum.DOCUMENT && theResource.getTypeElement().getValueAsEnum() != BundleTypeEnum.COLLECTION) { + String message = "Unable to store a Bundle resource on this server with a Bundle.type of: " + (theResource.getTypeElement().getValueAsEnum() != null ? theResource.getTypeElement().getValueAsEnum().getCode() : "(missing)"); throw new UnprocessableEntityException(message); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java index 67286c0b6fb..5a245ccc9b6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoBundleDstu3.java @@ -32,8 +32,8 @@ public class FhirResourceDaoBundleDstu3 extends FhirResourceDaoDstu3 { protected void preProcessResourceForStorage(Bundle theResource) { super.preProcessResourceForStorage(theResource); - if (theResource.getType() != BundleType.DOCUMENT) { - String message = "Unable to store a Bundle resource on this server with a Bundle.type value other than '" + BundleType.DOCUMENT.toCode() + "' - Value was: " + (theResource.getType() != null ? theResource.getType().toCode() : "(missing)"); + if (theResource.getType() != BundleType.DOCUMENT && theResource.getType() != BundleType.COLLECTION) { + String message = "Unable to store a Bundle resource on this server with a Bundle.type value of: " + (theResource.getType() != null ? theResource.getType().toCode() : "(missing)"); throw new UnprocessableEntityException(message); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index 2f1ee57ca38..60ba5fef066 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -87,6 +87,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { @Qualifier("myConceptMapDaoDstu2") protected IFhirResourceDao myConceptMapDao; @Autowired + @Qualifier("myBundleDaoDstu2") + protected IFhirResourceDao myBundleDao; + @Autowired @Qualifier("myMedicationDaoDstu2") protected IFhirResourceDao myMedicationDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index fc0e60aa7c1..9b4adac25a9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -34,6 +34,7 @@ import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; +import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; @@ -73,6 +74,7 @@ import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Questionnaire; import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; +import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum; import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum; @@ -106,7 +108,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; @SuppressWarnings("unchecked") @@ -129,6 +130,49 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } + @Test + public void testCreateBundleAllowsDocumentAndCollection() { + String methodName = "testCreateBundleAllowsDocumentAndCollection"; + + Patient p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue(methodName); + IIdType pid = myPatientDao.create(p, mySrd).getId(); + p.setId(pid); + ourLog.info("Created patient, got it: {}", pid); + + Bundle bundle = new Bundle(); + bundle.setType((BundleTypeEnum)null); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: (missing)", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.BATCH_RESPONSE); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: batch-response", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.COLLECTION); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.DOCUMENT); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + } + + /** * This gets called from assertGone too! Careful about exceptions... */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index 2973bb44946..5b7a8ad0165 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -93,6 +93,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { @Qualifier("myCodeSystemDaoDstu3") protected IFhirResourceDao myCodeSystemDao; @Autowired + @Qualifier("myBundleDaoDstu3") + protected IFhirResourceDao myBundleDao; + @Autowired @Qualifier("myConceptMapDaoDstu3") protected IFhirResourceDao myConceptMapDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index a2ec5de06fd..61368051f1e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -36,6 +36,7 @@ import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; import org.hl7.fhir.dstu3.model.BaseResource; import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Bundle.BundleType; import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.CodeableConcept; @@ -532,6 +533,48 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } } + @Test + public void testCreateBundleAllowsDocumentAndCollection() { + String methodName = "testCreateBundleAllowsDocumentAndCollection"; + + Patient p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue(methodName); + IIdType pid = myPatientDao.create(p, mySrd).getId(); + p.setId(pid); + ourLog.info("Created patient, got it: {}", pid); + + Bundle bundle = new Bundle(); + bundle.setType(null); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type value of: (missing)", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleType.SEARCHSET); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type value of: searchset", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleType.COLLECTION); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + bundle = new Bundle(); + bundle.setType(BundleType.DOCUMENT); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + } + @Test public void testCreateWithIfNoneExistBasic() { String methodName = "testCreateWithIfNoneExistBasic"; diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 9f8d8105d24..1274aec7b08 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -188,7 +188,9 @@ deviceusestatement diagnosticorder diagnosticreport + documentmanifest documentreference eligibilityrequest diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java index 417839a9564..3842cc47fde 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java @@ -1475,6 +1475,30 @@ public class XmlParserDstu2Test { } + @Test + public void testEncodeWithEncodeElementsMandatory() throws Exception { + MedicationOrder mo = new MedicationOrder(); + mo.getText().setDiv("
    DIV
    "); + mo.setNote("NOTE"); + mo.setMedication(new ResourceReferenceDt("Medication/123")); + + ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + bundle.setTotal(100); + bundle.addEntry().setResource(mo); + + { + IParser p = ourCtx.newXmlParser(); + p.setEncodeElements(new HashSet(Arrays.asList("Bundle.entry", "*.text", "*.(mandatory)"))); + p.setPrettyPrint(true); + String out = p.encodeResourceToString(bundle); + ourLog.info(out); + assertThat(out, (containsString("DIV"))); + assertThat(out, (containsString("Medication/123"))); + assertThat(out, not(containsString("NOTE"))); + } + } + + @Test public void testEncodeWithEncodeElements() throws Exception { Patient patient = new Patient(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamTest.java index e0c75eb35be..a20a517a4ba 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.server; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -15,6 +16,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -22,6 +24,8 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; +import ca.uhn.fhir.model.dstu2.resource.MedicationOrder; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum; import ca.uhn.fhir.model.primitive.IdDt; @@ -79,11 +83,27 @@ public class SummaryParamTest { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(Constants.CT_HTML_WITH_UTF8.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf")); assertThat(responseContent, not(containsString("THE DIV", responseContent); + assertThat(responseContent, not(containsString("efer"))); + assertEquals(SummaryEnum.TEXT, ourLastSummary); + } + + @Test + public void testReadSummaryTextWithMandatory() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + assertEquals(Constants.CT_HTML_WITH_UTF8.replace(" ", "").toLowerCase(), status.getEntity().getContentType().getValue().replace(" ", "").replace("UTF", "utf")); + assertThat(responseContent, not(containsString("TEXT", responseContent); assertThat(responseContent, not(containsString("family"))); assertThat(responseContent, not(containsString("maritalStatus"))); - assertEquals(SummaryEnum.TEXT, ourLastSummary); } @Test @@ -170,6 +190,22 @@ public class SummaryParamTest { assertEquals(SummaryEnum.TEXT, ourLastSummary); } + @Test + public void testSearchSummaryTextWithMandatory() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, (containsString(""))); + assertThat(responseContent, (containsString("entry"))); + assertThat(responseContent, (containsString(">TEXT<"))); + assertThat(responseContent, (containsString("Medication/123"))); + assertThat(responseContent, not(containsStringIgnoringCase("note"))); + } + @Test public void testSearchSummaryTextMulti() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode()); @@ -226,12 +262,10 @@ public class SummaryParamTest { ourPort = PortUtil.findFreePort(); ourServer = new Server(ourPort); - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); + servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationOrderProvider()); ServletHolder servletHolder = new ServletHolder(servlet); proxyHandler.addServletWithMapping(servletHolder, "/*"); ourServer.setHandler(proxyHandler); @@ -244,6 +278,30 @@ public class SummaryParamTest { } + public static class DummyMedicationOrderProvider implements IResourceProvider{ + + @Override + public Class getResourceType() { + return MedicationOrder.class; + } + + @Read + public MedicationOrder read(@IdParam IdDt theId) { + MedicationOrder retVal = new MedicationOrder(); + retVal.getText().setDiv("
    TEXT
    "); + retVal.getNoteElement().setValue("NOTE"); + retVal.setMedication(new ResourceReferenceDt("Medication/123")); + retVal.setId(theId); + return retVal; + } + + @Search + public List read() { + return Arrays.asList(read(new IdDt("999"))); + } + + } + public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/src/changes/changes.xml b/src/changes/changes.xml index bf59c9d4d5d..80c90b067fc 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -426,6 +426,16 @@ Improve CLI error message if the tool can't bind to the requested port. Thanks to Claude Nanjo for the suggestion! + + Server param of _summary=text]]> did not + include mandatory elements in return as well as + the text element, even though the FHIR specification + required it. + + + Remove invalid resource type "Documentation" from DSTU2 + structures. +