diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 8ad4c34658a..a34b1f8f8c7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -66,6 +66,7 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.server.Constants; @@ -1720,10 +1721,18 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { assertEquals(id.withVersion("2").getValue(), history.getEntry().get(1).getResource().getId()); assertEquals(HTTPVerb.DELETE, history.getEntry().get(1).getRequest().getMethodElement().getValue()); + assertEquals("http://localhost:" + ourPort + "/fhir/context/Patient/" + id.getIdPart() + "/_history/2", history.getEntry().get(1).getRequest().getUrl()); assertEquals(0, ((Patient) history.getEntry().get(1).getResource()).getName().size()); assertEquals(id.withVersion("1").getValue(), history.getEntry().get(2).getResource().getId()); assertEquals(1, ((Patient) history.getEntry().get(2).getResource()).getName().size()); + + try { + myBundleDao.validate(history, null, null, null, null, null, mySrd); + } catch (PreconditionFailedException e) { + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); + throw e; + } } /** diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index 0855fefb090..602436df57a 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -21,13 +21,7 @@ package org.hl7.fhir.dstu3.hapi.rest.server; */ import static org.apache.commons.lang3.StringUtils.isNotBlank; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.Bundle; @@ -35,25 +29,17 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode; -import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.dstu3.model.DomainResource; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; -import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.server.BundleInclusionRule; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.EncodingEnum; -import ca.uhn.fhir.rest.server.IBundleProvider; -import ca.uhn.fhir.rest.server.IPagingProvider; -import ca.uhn.fhir.rest.server.IRestfulServer; -import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory; -import ca.uhn.fhir.rest.server.RestfulServerUtils; +import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.ResourceReferenceInfo; @@ -135,6 +121,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); if (httpVerb != null) { entry.getRequest().getMethodElement().setValueAsString(httpVerb); + entry.getRequest().getUrlElement().setValue(next.getId()); } } @@ -161,7 +148,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { for (IBaseResource next : theResult) { if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add((IdType) next.getIdElement()); + addedResourceIds.add(next.getIdElement()); } } @@ -221,11 +208,14 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); Resource nextAsResource = (Resource)next; + IIdType id = populateBundleEntryFullUrl(next, entry); String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource); if (httpVerb != null) { entry.getRequest().getMethodElement().setValueAsString(httpVerb); + if (id != null) { + entry.getRequest().setUrl(id.getValue()); + } } - populateBundleEntryFullUrl(next, entry); String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); if (searchMode != null) { @@ -244,16 +234,19 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { } - private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + IIdType idElement = null; if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().toVersionless().getValue()); + idElement = next.getIdElement(); + entry.setFullUrl(idElement.toVersionless().getValue()); } else { if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { - IIdType id = next.getIdElement().toVersionless(); - id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); - entry.setFullUrl(id.getValue()); + idElement = next.getIdElement(); + idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); + entry.setFullUrl(idElement.toVersionless().getValue()); } } + return idElement; } @Override @@ -392,7 +385,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { Resource next = (Resource) nextBaseRes; BundleEntryComponent nextEntry = myBundle.addEntry(); - nextEntry.setResource((Resource) next); + nextEntry.setResource(next); if (next.getIdElement().isEmpty()) { nextEntry.getRequest().setMethod(HTTPVerb.POST); } else { diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d8e56d94f25..ebd29ae6a7c 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -81,6 +81,11 @@ DELETE fails because of constraint issues, instead of HTTP 400 Invalid Request]]> + + Server history operation did not populate the Bundle.entry.request.url + field, which is required in order for the bundle to pass validation. + Thanks to Richard Kavanaugh for spotting this! +