diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 5edacfafad4..eec9b6aca47 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -894,7 +894,7 @@ public abstract class BaseHapiFhirDao implements IDao { } } - protected MetaDt toMetaDt(List tagDefinitions) { + protected MetaDt toMetaDt(Collection tagDefinitions) { MetaDt retVal = new MetaDt(); for (TagDefinition next : tagDefinitions) { switch (next.getTagType()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 67f2f36dae0..aa5857a7dc3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -1733,9 +1733,10 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } myEntityManager.merge(entity); - + myEntityManager.flush(); + ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() }); - + return metaGetOperation(theResourceId); } @@ -1761,16 +1762,16 @@ public abstract class BaseHapiFhirResourceDao extends BaseH ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName()); notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - Long pid = super.translateForcedIdToPid(theId); - - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type AND t.myResourceId = :res_id)"; - TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); - q.setParameter("res_type", myResourceName); - q.setParameter("res_id", pid); - List tagDefinitions = q.getResultList(); - - MetaDt retVal = super.toMetaDt(tagDefinitions); + Set tagDefs = new HashSet(); + BaseHasResource entity = readEntity(theId); + for (BaseTag next : entity.getTags()) { + tagDefs.add(next.getTag()); + } + MetaDt retVal = super.toMetaDt(tagDefs); + retVal.setLastUpdated(entity.getUpdated()); + retVal.setVersionId(Long.toString(entity.getVersion())); + return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceHistoryTag.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceHistoryTag.java index c617ec2d53f..633520119c4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceHistoryTag.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceHistoryTag.java @@ -77,9 +77,10 @@ public class ResourceHistoryTag extends BaseTag implements Serializable { public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag) { - myResourceHistory=theResourceHistoryTable; setTag(theTag); + setResource(theResourceHistoryTable); setResourceId(theResourceHistoryTable.getResourceId()); + setResourceType(theResourceHistoryTable.getResourceType()); } public ResourceHistoryTable getResourceHistory() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java index 6548672499c..8172396ec18 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java @@ -143,7 +143,6 @@ public class ResourceTable extends BaseHasResource implements Serializable { public ResourceTag addTag(TagDefinition theTag) { ResourceTag tag = new ResourceTag(this, theTag); - tag.setResourceType(getResourceType()); getTags().add(tag); return tag; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTag.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTag.java index 26ad960e9e1..45f2b0307d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTag.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTag.java @@ -65,9 +65,10 @@ public class ResourceTag extends BaseTag { } public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag) { - myResource = theResourceTable; - myResourceId = theResourceTable.getId(); setTag(theTag); + setResource(theResourceTable); + setResourceId(theResourceTable.getId()); + setResourceType(theResourceTable.getResourceType()); } public ResourceTable getResource() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java index b2a0ee07bd0..8359b39134e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java @@ -230,8 +230,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { @Test public void testCreateOperationOutcome() { /* - * If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code - * value across versions. We store the string as a constant, so something will need to be fixed. + * If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code value across versions. We store the string as a constant, so something will need to + * be fixed. */ assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR); assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR); @@ -311,7 +311,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { Patient p = new Patient(); p.addName().addFamily("Hello"); p.getManagingOrganization().setReference("Organization/"); - + try { myPatientDao.create(p); fail(); @@ -325,7 +325,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { Patient p = new Patient(); p.addName().addFamily("Hello"); p.getManagingOrganization().setReference("Blah/123"); - + try { myPatientDao.create(p); fail(); @@ -339,7 +339,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { Patient p = new Patient(); p.addName().addFamily("Hello"); p.getManagingOrganization().setReference("123"); - + try { myPatientDao.create(p); fail(); @@ -348,8 +348,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } - - @Test public void testCreateWithIfNoneExistBasic() { String methodName = "testCreateWithIfNoneExistBasic"; @@ -1108,6 +1106,141 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } + @Test + public void testInstanceMetaOperations() { + String methodName = "testMetaRead"; + IIdType id; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue(methodName); + TagList tagList = new TagList(); + tagList.addTag("tag_scheme1", "tag_code1", "tag_display1"); + tagList.addTag("tag_scheme2", "tag_code2", "tag_display2"); + ResourceMetadataKeyEnum.TAG_LIST.put(patient, tagList); + + List securityLabels = new ArrayList(); + securityLabels.add(new CodingDt().setSystem("seclabel_sys1").setCode("seclabel_code1").setDisplay("seclabel_dis1")); + securityLabels.add(new CodingDt().setSystem("seclabel_sys2").setCode("seclabel_code2").setDisplay("seclabel_dis2")); + ResourceMetadataKeyEnum.SECURITY_LABELS.put(patient, securityLabels); + + ArrayList profiles = new ArrayList(); + profiles.add(new IdDt("http://profile/1")); + profiles.add(new IdDt("http://profile/2")); + ResourceMetadataKeyEnum.PROFILES.put(patient, profiles); + + id = myPatientDao.create(patient).getId(); + } + + assertTrue(id.hasVersionIdPart()); + + /* + * Create a second version + */ + + Patient pt = myPatientDao.read(id); + pt.addName().addFamily("anotherName"); + myPatientDao.update(pt); + + /* + * Meta-Delete on previous version + */ + + MetaDt meta = new MetaDt(); + meta.addTag().setSystem("tag_scheme1").setCode("tag_code1"); + meta.addProfile("http://profile/1"); + meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1"); + MetaDt newMeta = myPatientDao.metaDeleteOperation(id.withVersion("1"), meta); + assertEquals(1, newMeta.getProfile().size()); + assertEquals(1, newMeta.getSecurity().size()); + assertEquals(1, newMeta.getTag().size()); + assertEquals("tag_code2", newMeta.getTag().get(0).getCode()); + assertEquals("http://profile/2", newMeta.getProfile().get(0).getValue()); + assertEquals("seclabel_code2", newMeta.getSecurity().get(0).getCode()); + + /* + * Meta Read on Version + */ + + meta = myPatientDao.metaGetOperation(id.withVersion("1")); + assertEquals(1, meta.getProfile().size()); + assertEquals(1, meta.getSecurity().size()); + assertEquals(1, meta.getTag().size()); + assertEquals("tag_code2", meta.getTag().get(0).getCode()); + assertEquals("http://profile/2", meta.getProfile().get(0).getValue()); + assertEquals("seclabel_code2", meta.getSecurity().get(0).getCode()); + + /* + * Meta-read on Version 2 + */ + meta = myPatientDao.metaGetOperation(id.withVersion("2")); + assertEquals(2, meta.getProfile().size()); + assertEquals(2, meta.getSecurity().size()); + assertEquals(2, meta.getTag().size()); + + /* + * Meta-read on latest version + */ + meta = myPatientDao.metaGetOperation(id.toVersionless()); + assertEquals(2, meta.getProfile().size()); + assertEquals(2, meta.getSecurity().size()); + assertEquals(2, meta.getTag().size()); + assertEquals("2", meta.getVersionId()); + + /* + * Meta-Add on previous version + */ + + meta = new MetaDt(); + meta.addTag().setSystem("tag_scheme1").setCode("tag_code1"); + meta.addProfile("http://profile/1"); + meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1"); + newMeta = myPatientDao.metaAddOperation(id.withVersion("1"), meta); + assertEquals(2, newMeta.getProfile().size()); + assertEquals(2, newMeta.getSecurity().size()); + assertEquals(2, newMeta.getTag().size()); + + /* + * Meta Read on Version + */ + + meta = myPatientDao.metaGetOperation(id.withVersion("1")); + assertEquals(2, meta.getProfile().size()); + assertEquals(2, meta.getSecurity().size()); + assertEquals(2, meta.getTag().size()); + assertEquals("1", meta.getVersionId()); + + /* + * Meta delete on latest + */ + + meta = new MetaDt(); + meta.addTag().setSystem("tag_scheme1").setCode("tag_code1"); + meta.addProfile("http://profile/1"); + meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1"); + newMeta = myPatientDao.metaDeleteOperation(id.toVersionless(), meta); + assertEquals(1, newMeta.getProfile().size()); + assertEquals(1, newMeta.getSecurity().size()); + assertEquals(1, newMeta.getTag().size()); + assertEquals("tag_code2", newMeta.getTag().get(0).getCode()); + assertEquals("http://profile/2", newMeta.getProfile().get(0).getValue()); + assertEquals("seclabel_code2", newMeta.getSecurity().get(0).getCode()); + + /* + * Meta-Add on latest version + */ + + meta = new MetaDt(); + meta.addTag().setSystem("tag_scheme1").setCode("tag_code1"); + meta.addProfile("http://profile/1"); + meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1"); + newMeta = myPatientDao.metaAddOperation(id.toVersionless(), meta); + assertEquals(2, newMeta.getProfile().size()); + assertEquals(2, newMeta.getSecurity().size()); + assertEquals(2, newMeta.getTag().size()); + assertEquals("2", newMeta.getVersionId()); + + } + @Test public void testResourceInstanceMetaOperation() { @@ -1370,7 +1503,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertEquals(BundleEntrySearchModeEnum.INCLUDE, ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get((IResource) results.get(1))); } - @Test public void testSortByDate() { Patient p = new Patient(); @@ -1841,5 +1973,4 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } - } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index 32745ab5f6b..1ff2600579d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -90,6 +90,7 @@ import ca.uhn.fhir.model.dstu2.valueset.AnswerFormatEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum; import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; +import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum; import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; @@ -827,10 +828,21 @@ public class ResourceProviderDstu2Test extends BaseJpaTest { p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02"); ourClient.create().resource(p2).execute().getId(); - Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute(); - assertEquals(1, actual.size()); - assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); - assertEquals(BundleEntrySearchModeEnum.MATCH, actual.getEntries().get(0).getSearchMode().getValueAsEnum()); + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle actual = ourClient + .search() + .forResource(Patient.class) + .where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")) + .encodedJson() + .prettyPrint() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + + assertEquals(1, actual.getEntry().size()); + assertEquals(ourServerBase + "/Patient/" + p1Id.getIdPart(), actual.getEntry().get(0).getFullUrl()); + assertEquals(p1Id.getIdPart(), actual.getEntry().get(0).getResource().getId().getIdPart()); + assertEquals(SearchEntryModeEnum.MATCH, actual.getEntry().get(0).getSearch().getModeElement().getValueAsEnum()); } @Test diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index 9beb842d3a7..d32f16589f3 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -69,6 +69,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class); private Bundle myBundle; private FhirContext myContext; + private String myBase; public Dstu2BundleFactory(FhirContext theContext) { myContext = theContext; @@ -237,9 +238,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { } while (references.isEmpty() == false); Entry entry = myBundle.addEntry().setResource(next); - if (next.getId().hasBaseUrl()) { - entry.setFullUrl(next.getId().getValue()); - } + populateBundleEntryFullUrl(next, entry); BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); if (searchMode != null) { @@ -253,16 +252,28 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { for (IResource next : includedResources) { Entry entry = myBundle.addEntry(); entry.setResource(next).getSearch().setMode(SearchEntryModeEnum.INCLUDE); - if (next.getId().hasBaseUrl()) { - entry.setFullUrl(next.getId().getValue()); - } + populateBundleEntryFullUrl(next, entry); } } + private void populateBundleEntryFullUrl(IResource next, Entry entry) { + if (next.getId().hasBaseUrl()) { + entry.setFullUrl(next.getId().toVersionless().getValue()); + } else { + if (isNotBlank(myBase) && next.getId().hasIdPart()) { + IdDt id = next.getId().toVersionless(); + id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); + entry.setFullUrl(id.getValue()); + } + } + } + @Override public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType theLastUpdated) { + myBase = theServerBase; + if (myBundle.getId().isEmpty()) { myBundle.setId(UUID.randomUUID().toString()); } @@ -306,6 +317,8 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { @Override public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set theIncludes) { + myBase = theServerBase; + int numToReturn; String searchId = null; List resourceList; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java index 00b22ff477f..ca942ad5f64 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java @@ -19,7 +19,8 @@ package ca.uhn.fhir.rest.server.provider.dstu2hl7org; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.ArrayList; import java.util.Collections; @@ -64,15 +65,15 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { - private String myBase; - private Bundle myBundle; - private FhirContext myContext; + private String myBase; + private Bundle myBundle; + private FhirContext myContext; - public Dstu2Hl7OrgBundleFactory(FhirContext theContext) { - myContext = theContext; - } + public Dstu2Hl7OrgBundleFactory(FhirContext theContext) { + myContext = theContext; + } - private void addResourcesForSearch(List theResult) { + private void addResourcesForSearch(List theResult) { List includedResources = new ArrayList(); Set addedResourceIds = new HashSet(); @@ -131,290 +132,310 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { } while (references.isEmpty() == false); BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); - IdType nextId = (IdType) next.getIdElement(); - if (isNotBlank(myBase) && isNotBlank(nextId.getResourceType())) { - entry.setFullUrlElement(nextId.withServerBase(myBase, nextId.getResourceType())); - } - + populateBundleEntryFullUrl(next, entry); } /* * Actually add the resources to the bundle */ for (IBaseResource next : includedResources) { - myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + BundleEntryComponent entry = myBundle.addEntry(); + entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + populateBundleEntryFullUrl(next, entry); } } - @Override - public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { - if (myBundle == null) { - myBundle = new Bundle(); - } + private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + if (next.getIdElement().hasBaseUrl()) { + entry.setFullUrl(next.getIdElement().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()); + } + } + } - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); + @Override + public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, + BundleInclusionRule theBundleInclusionRule, Set theIncludes) { + if (myBundle == null) { + myBundle = new Bundle(); + } - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } + List includedResources = new ArrayList(); + Set addedResourceIds = new HashSet(); - for (IBaseResource next : theResult) { + for (IBaseResource next : theResult) { + if (next.getIdElement().isEmpty() == false) { + addedResourceIds.add(next.getIdElement()); + } + } - List contained; - if (next instanceof IDomainResource) { - IDomainResource nextDomain = (IDomainResource) next; - contained = nextDomain.getContained(); - } else { - contained = Collections.emptyList(); - } + for (IBaseResource next : theResult) { - Set containedIds = new HashSet(); - for (IAnyResource nextContained : contained) { - if (nextContained.getId().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } + List contained; + if (next instanceof IDomainResource) { + IDomainResource nextDomain = (IDomainResource) next; + contained = nextDomain.getContained(); + } else { + contained = Collections.emptyList(); + } - List references = myContext.newTerser().getAllResourceReferences(next); - do { - List addedResourcesThisPass = new ArrayList(); + Set containedIds = new HashSet(); + for (IAnyResource nextContained : contained) { + if (nextContained.getId().isEmpty() == false) { + containedIds.add(nextContained.getIdElement().getValue()); + } + } - for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) - continue; + List references = myContext.newTerser().getAllResourceReferences(next); + do { + List addedResourcesThisPass = new ArrayList(); - IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } + for (ResourceReferenceInfo nextRefInfo : references) { + if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) + continue; - IdType id = (IdType) nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } + IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource(); + if (nextRes != null) { + if (nextRes.getIdElement().hasIdPart()) { + if (containedIds.contains(nextRes.getIdElement().getValue())) { + // Don't add contained IDs as top level resources + continue; + } - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } + IdType id = (IdType) nextRes.getIdElement(); + if (id.hasResourceType() == false) { + String resName = myContext.getResourceDefinition(nextRes).getName(); + id = id.withResourceType(resName); + } - } - } - } + if (!addedResourceIds.contains(id)) { + addedResourceIds.add(id); + addedResourcesThisPass.add(nextRes); + } - includedResources.addAll(addedResourcesThisPass); + } + } + } - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IBaseResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllResourceReferences(iResource); - references.addAll(newReferences); - } - } while (references.isEmpty() == false); + includedResources.addAll(addedResourcesThisPass); - BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); + // Linked resources may themselves have linked resources + references = new ArrayList(); + for (IBaseResource iResource : addedResourcesThisPass) { + List newReferences = myContext.newTerser().getAllResourceReferences(iResource); + references.addAll(newReferences); + } + } while (references.isEmpty() == false); - // BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); - // if (searchMode != null) { - // entry.getSearch().getModeElement().setValue(searchMode.getCode()); - // } - } + BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - } + // BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); + // if (searchMode != null) { + // entry.getSearch().getModeElement().setValue(searchMode.getCode()); + // } + } - } + /* + * Actually add the resources to the bundle + */ + for (IBaseResource next : includedResources) { + myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + } - @Override - public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType theLastUpdated) { + } - if (isBlank(myBundle.getId())) { - myBundle.setId(UUID.randomUUID().toString()); - } + @Override + public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, + Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType theLastUpdated) { - if (myBundle.getMeta().getLastUpdated() == null) { - InstantType instantType = new InstantType(); - instantType.setValueAsString(theLastUpdated.getValueAsString()); - myBundle.getMeta().setLastUpdatedElement(instantType); - } + if (isBlank(myBundle.getId())) { + myBundle.setId(UUID.randomUUID().toString()); + } - if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) { - myBundle.addLink().setRelation("self").setUrl(theCompleteUrl); - } + if (myBundle.getMeta().getLastUpdated() == null) { + InstantType instantType = new InstantType(); + instantType.setValueAsString(theLastUpdated.getValueAsString()); + myBundle.getMeta().setLastUpdatedElement(instantType); + } - myBase = theServerBase; + if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) { + myBundle.addLink().setRelation("self").setUrl(theCompleteUrl); + } - if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - } + myBase = theServerBase; - if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) { - myBundle.getTotalElement().setValue(theTotalResults); - } - } + if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { + myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); + } - @Override - public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() { - return null; - } + if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) { + myBundle.getTotalElement().setValue(theTotalResults); + } + } - @Override - public IBaseResource getResourceBundle() { - return myBundle; - } + @Override + public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() { + return null; + } - private boolean hasLink(String theLinkType, Bundle theBundle) { - for (BundleLinkComponent next : theBundle.getLink()) { - if (theLinkType.equals(next.getRelation())) { - return true; - } - } - return false; - } + @Override + public IBaseResource getResourceBundle() { + return myBundle; + } - @Override - public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, - Set theIncludes) { - int numToReturn; - String searchId = null; - List resourceList; - if (theServer.getPagingProvider() == null) { - numToReturn = theResult.size(); - if (numToReturn == 0) { - resourceList = Collections.emptyList(); - } else { - resourceList = theResult.getResources(0, numToReturn); - RestfulServerUtils.validateResourceListNotNull(resourceList); - } - } else { - IPagingProvider pagingProvider = theServer.getPagingProvider(); - if (theLimit == null) { - numToReturn = pagingProvider.getDefaultPageSize(); - } else { - numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit); - } + private boolean hasLink(String theLinkType, Bundle theBundle) { + for (BundleLinkComponent next : theBundle.getLink()) { + if (theLinkType.equals(next.getRelation())) { + return true; + } + } + return false; + } - numToReturn = Math.min(numToReturn, theResult.size() - theOffset); - resourceList = theResult.getResources(theOffset, numToReturn + theOffset); - RestfulServerUtils.validateResourceListNotNull(resourceList); + @Override + public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, + EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, + int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set theIncludes) { + myBase = theServerBase; + int numToReturn; + String searchId = null; + List resourceList; + if (theServer.getPagingProvider() == null) { + numToReturn = theResult.size(); + if (numToReturn == 0) { + resourceList = Collections.emptyList(); + } else { + resourceList = theResult.getResources(0, numToReturn); + RestfulServerUtils.validateResourceListNotNull(resourceList); + } + } else { + IPagingProvider pagingProvider = theServer.getPagingProvider(); + if (theLimit == null) { + numToReturn = pagingProvider.getDefaultPageSize(); + } else { + numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit); + } - if (theSearchId != null) { - searchId = theSearchId; - } else { - if (theResult.size() > numToReturn) { - searchId = pagingProvider.storeResultList(theResult); - Validate.notNull(searchId, "Paging provider returned null searchId"); - } - } - } + numToReturn = Math.min(numToReturn, theResult.size() - theOffset); + resourceList = theResult.getResources(theOffset, numToReturn + theOffset); + RestfulServerUtils.validateResourceListNotNull(resourceList); - for (IBaseResource next : resourceList) { - if (next.getIdElement() == null || next.getIdElement().isEmpty()) { - if (!(next instanceof OperationOutcome)) { - throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IBaseResource#setId(IdDt) must be called)"); - } - } - } + if (theSearchId != null) { + searchId = theSearchId; + } else { + if (theResult.size() > numToReturn) { + searchId = pagingProvider.storeResultList(theResult); + Validate.notNull(searchId, "Paging provider returned null searchId"); + } + } + } - if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) { - for (IBaseResource nextRes : resourceList) { - RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes); - if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) { - RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase); - } - } - } + for (IBaseResource next : resourceList) { + if (next.getIdElement() == null || next.getIdElement().isEmpty()) { + if (!(next instanceof OperationOutcome)) { + throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + + "] with no ID specified (IBaseResource#setId(IdDt) must be called)"); + } + } + } - addResourcesToBundle(resourceList, theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); - addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished()); + if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) { + for (IBaseResource nextRes : resourceList) { + RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes); + if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) { + RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase); + } + } + } - if (theServer.getPagingProvider() != null) { - int limit; - limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize(); - limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize()); + addResourcesToBundle(resourceList, theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); + addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, + theResult.getPublished()); - if (searchId != null) { - if (theOffset + numToReturn < theResult.size()) { - myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint)); - } - if (theOffset > 0) { - int start = Math.max(0, theOffset - limit); - myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint)); - } - } - } - } + if (theServer.getPagingProvider() != null) { + int limit; + limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize(); + limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize()); - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { - myBundle = new Bundle(); + if (searchId != null) { + if (theOffset + numToReturn < theResult.size()) { + myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(RestfulServerUtils.createPagingLink(theIncludes, + theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint)); + } + if (theOffset > 0) { + int start = Math.max(0, theOffset - limit); + myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(RestfulServerUtils.createPagingLink( + theIncludes, theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint)); + } + } + } + } - myBundle.setId(UUID.randomUUID().toString()); + @Override + public void initializeBundleFromResourceList(String theAuthor, List theResources, + String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { + myBundle = new Bundle(); - myBundle.getMeta().setLastUpdatedElement(InstantType.withCurrentTime()); + myBundle.setId(UUID.randomUUID().toString()); - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); + myBundle.getMeta().setLastUpdatedElement(InstantType.withCurrentTime()); - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - IBaseResource next = (IBaseResource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); + myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); + myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); + myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - nextEntry.setResource((Resource) next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getIdElement().getValue()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } + if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { + for (IBaseResource nextBaseRes : theResources) { + IBaseResource next = (IBaseResource) nextBaseRes; + BundleEntryComponent nextEntry = myBundle.addEntry(); - myBundle.getTotalElement().setValue(theTotalResults); - } + nextEntry.setResource((Resource) next); + if (next.getIdElement().isEmpty()) { + nextEntry.getRequest().setMethod(HTTPVerb.POST); + } else { + nextEntry.getRequest().setMethod(HTTPVerb.PUT); + if (next.getIdElement().isAbsolute()) { + nextEntry.getRequest().setUrl(next.getIdElement().getValue()); + } else { + String resourceType = myContext.getResourceDefinition(next).getName(); + nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), + next.getIdElement().getVersionIdPart()).getValue()); + } + } + } + } else { + addResourcesForSearch(theResources); + } - @Override - public void initializeWithBundleResource(IBaseResource theBundle) { - myBundle = (Bundle) theBundle; - } + myBundle.getTotalElement().setValue(theTotalResults); + } - @Override - public List toListOfResources() { - ArrayList retVal = new ArrayList(); - for (BundleEntryComponent next : myBundle.getEntry()) { - if (next.getResource() != null) { - retVal.add(next.getResource()); - } else if (next.getResponse().getLocationElement().isEmpty() == false) { - IdType id = new IdType(next.getResponse().getLocation()); - String resourceType = id.getResourceType(); - if (isNotBlank(resourceType)) { - IBaseResource res = (IBaseResource) myContext.getResourceDefinition(resourceType).newInstance(); - res.setId(id); - retVal.add(res); - } - } - } - return retVal; - } + @Override + public void initializeWithBundleResource(IBaseResource theBundle) { + myBundle = (Bundle) theBundle; + } + + @Override + public List toListOfResources() { + ArrayList retVal = new ArrayList(); + for (BundleEntryComponent next : myBundle.getEntry()) { + if (next.getResource() != null) { + retVal.add(next.getResource()); + } else if (next.getResponse().getLocationElement().isEmpty() == false) { + IdType id = new IdType(next.getResponse().getLocation()); + String resourceType = id.getResourceType(); + if (isNotBlank(resourceType)) { + IBaseResource res = (IBaseResource) myContext.getResourceDefinition(resourceType).newInstance(); + res.setId(id); + retVal.add(res); + } + } + } + return retVal; + } } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 10456c43b69..320b849756d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -151,6 +151,14 @@ security labels were not always properly removed by an update that was trying to remove them. Also don't store duplicates. + + Instance $meta operations on JPA server did not previously return the + resource version and lastUpdated time + + + Server responses populate Bundle.entry.fullUrl if possible. Thanks + to Bill de Beaubien for reporting! +