Don't preserve history mode (#5306)
* No history mode * Spotless * Version history * Fix changelog * Address review comment
This commit is contained in:
parent
d4a57fc123
commit
de341a5bb7
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 5306
|
||||||
|
title: "A new option has been added to the JPA server JpaStorageOptions which prevents the
|
||||||
|
server from maintaining a version history. In this mode, when a new version of a resource
|
||||||
|
is added, the previous version is automatically expunged."
|
|
@ -1465,12 +1465,43 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
boolean versionedTags =
|
boolean versionedTags =
|
||||||
getStorageSettings().getTagStorageMode() == JpaStorageSettings.TagStorageModeEnum.VERSIONED;
|
getStorageSettings().getTagStorageMode() == JpaStorageSettings.TagStorageModeEnum.VERSIONED;
|
||||||
|
|
||||||
final ResourceHistoryTable historyEntry = theEntity.toHistory(versionedTags);
|
ResourceHistoryTable historyEntry = null;
|
||||||
|
long resourceVersion = theEntity.getVersion();
|
||||||
|
boolean reusingHistoryEntity = false;
|
||||||
|
if (!myStorageSettings.isResourceDbHistoryEnabled() && resourceVersion > 1L) {
|
||||||
|
/*
|
||||||
|
* If we're not storing history, then just pull the current history
|
||||||
|
* table row and update it. Note that there is always a chance that
|
||||||
|
* this could return null if the current resourceVersion has been expunged
|
||||||
|
* in which case we'll still create a new one
|
||||||
|
*/
|
||||||
|
historyEntry = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(
|
||||||
|
theEntity.getResourceId(), resourceVersion - 1);
|
||||||
|
if (historyEntry != null) {
|
||||||
|
reusingHistoryEntity = true;
|
||||||
|
theEntity.populateHistoryEntityVersionAndDates(historyEntry);
|
||||||
|
if (versionedTags && theEntity.isHasTags()) {
|
||||||
|
for (ResourceTag next : theEntity.getTags()) {
|
||||||
|
historyEntry.addTag(next.getTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This should basically always be null unless resource history
|
||||||
|
* is disabled on this server. In that case, we'll just be reusing
|
||||||
|
* the previous version entity.
|
||||||
|
*/
|
||||||
|
if (historyEntry == null) {
|
||||||
|
historyEntry = theEntity.toHistory(versionedTags);
|
||||||
|
}
|
||||||
|
|
||||||
historyEntry.setEncoding(theChanged.getEncoding());
|
historyEntry.setEncoding(theChanged.getEncoding());
|
||||||
historyEntry.setResource(theChanged.getResourceBinary());
|
historyEntry.setResource(theChanged.getResourceBinary());
|
||||||
historyEntry.setResourceTextVc(theChanged.getResourceText());
|
historyEntry.setResourceTextVc(theChanged.getResourceText());
|
||||||
|
|
||||||
ourLog.debug("Saving history entry {}", historyEntry.getIdDt());
|
ourLog.debug("Saving history entry ID[{}] for RES_ID[{}]", historyEntry.getId(), historyEntry.getResourceId());
|
||||||
myResourceHistoryTableDao.save(historyEntry);
|
myResourceHistoryTableDao.save(historyEntry);
|
||||||
theEntity.setCurrentVersionEntity(historyEntry);
|
theEntity.setCurrentVersionEntity(historyEntry);
|
||||||
|
|
||||||
|
@ -1503,7 +1534,18 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
boolean haveSource = isNotBlank(source) && shouldStoreSource;
|
boolean haveSource = isNotBlank(source) && shouldStoreSource;
|
||||||
boolean haveRequestId = isNotBlank(requestId) && shouldStoreRequestId;
|
boolean haveRequestId = isNotBlank(requestId) && shouldStoreRequestId;
|
||||||
if (haveSource || haveRequestId) {
|
if (haveSource || haveRequestId) {
|
||||||
ResourceHistoryProvenanceEntity provenance = new ResourceHistoryProvenanceEntity();
|
ResourceHistoryProvenanceEntity provenance = null;
|
||||||
|
if (reusingHistoryEntity) {
|
||||||
|
/*
|
||||||
|
* If version history is disabled, then we may be reusing
|
||||||
|
* a previous history entity. If that's the case, let's try
|
||||||
|
* to reuse the previous provenance entity too.
|
||||||
|
*/
|
||||||
|
provenance = historyEntry.getProvenance();
|
||||||
|
}
|
||||||
|
if (provenance == null) {
|
||||||
|
provenance = new ResourceHistoryProvenanceEntity();
|
||||||
|
}
|
||||||
provenance.setResourceHistoryTable(historyEntry);
|
provenance.setResourceHistoryTable(historyEntry);
|
||||||
provenance.setResourceTable(theEntity);
|
provenance.setResourceTable(theEntity);
|
||||||
provenance.setPartitionId(theEntity.getPartitionId());
|
provenance.setPartitionId(theEntity.getPartitionId());
|
||||||
|
|
|
@ -71,12 +71,21 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
|
||||||
@Column(name = "RES_ID", nullable = false)
|
@Column(name = "RES_ID", nullable = false)
|
||||||
private Long myResourceId;
|
private Long myResourceId;
|
||||||
|
|
||||||
public ResourceHistoryTag() {}
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public ResourceHistoryTag() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public ResourceHistoryTag(
|
public ResourceHistoryTag(
|
||||||
ResourceHistoryTable theResourceHistoryTable,
|
ResourceHistoryTable theResourceHistoryTable,
|
||||||
TagDefinition theTag,
|
TagDefinition theTag,
|
||||||
PartitionablePartitionId theRequestPartitionId) {
|
PartitionablePartitionId theRequestPartitionId) {
|
||||||
|
this();
|
||||||
setTag(theTag);
|
setTag(theTag);
|
||||||
setResource(theResourceHistoryTable);
|
setResource(theResourceHistoryTable);
|
||||||
setResourceId(theResourceHistoryTable.getResourceId());
|
setResourceId(theResourceHistoryTable.getResourceId());
|
||||||
|
|
|
@ -874,13 +874,8 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
||||||
|
|
||||||
retVal.setResourceId(myId);
|
retVal.setResourceId(myId);
|
||||||
retVal.setResourceType(myResourceType);
|
retVal.setResourceType(myResourceType);
|
||||||
retVal.setVersion(getVersion());
|
|
||||||
retVal.setTransientForcedId(getTransientForcedId());
|
retVal.setTransientForcedId(getTransientForcedId());
|
||||||
|
|
||||||
retVal.setPublished(getPublishedDate());
|
|
||||||
retVal.setUpdated(getUpdatedDate());
|
|
||||||
retVal.setFhirVersion(getFhirVersion());
|
retVal.setFhirVersion(getFhirVersion());
|
||||||
retVal.setDeleted(getDeleted());
|
|
||||||
retVal.setResourceTable(this);
|
retVal.setResourceTable(this);
|
||||||
retVal.setForcedId(getForcedId());
|
retVal.setForcedId(getForcedId());
|
||||||
retVal.setPartitionId(getPartitionId());
|
retVal.setPartitionId(getPartitionId());
|
||||||
|
@ -892,9 +887,23 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
populateHistoryEntityVersionAndDates(retVal);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates several temporal values in a {@link ResourceHistoryTable} entity which
|
||||||
|
* are pulled from this entity, including the resource version, and the
|
||||||
|
* creation, update, and deletion dates.
|
||||||
|
*/
|
||||||
|
public void populateHistoryEntityVersionAndDates(ResourceHistoryTable theResourceHistoryTable) {
|
||||||
|
theResourceHistoryTable.setVersion(getVersion());
|
||||||
|
theResourceHistoryTable.setPublished(getPublishedDate());
|
||||||
|
theResourceHistoryTable.setUpdated(getUpdatedDate());
|
||||||
|
theResourceHistoryTable.setDeleted(getDeleted());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||||
|
|
|
@ -19,7 +19,9 @@ import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider;
|
||||||
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper;
|
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao;
|
||||||
|
@ -292,6 +294,12 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil
|
||||||
@Autowired
|
@Autowired
|
||||||
protected IResourceHistoryTableDao myResourceHistoryTableDao;
|
protected IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
protected IResourceTagDao myResourceTagDao;
|
||||||
|
@Autowired
|
||||||
|
protected IResourceHistoryTagDao myResourceHistoryTagDao;
|
||||||
|
@Autowired
|
||||||
|
protected IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao;
|
||||||
|
@Autowired
|
||||||
protected IForcedIdDao myForcedIdDao;
|
protected IForcedIdDao myForcedIdDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myCoverageDaoR5")
|
@Qualifier("myCoverageDaoR5")
|
||||||
|
@ -315,8 +323,6 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil
|
||||||
@Qualifier("myResourceProvidersR5")
|
@Qualifier("myResourceProvidersR5")
|
||||||
protected ResourceProviderFactory myResourceProviders;
|
protected ResourceProviderFactory myResourceProviders;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected IResourceTagDao myResourceTagDao;
|
|
||||||
@Autowired
|
|
||||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.r5;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
|
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.util.BundleBuilder;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r5.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
|
import org.hl7.fhir.r5.model.CodeType;
|
||||||
|
import org.hl7.fhir.r5.model.IdType;
|
||||||
|
import org.hl7.fhir.r5.model.Parameters;
|
||||||
|
import org.hl7.fhir.r5.model.Meta;
|
||||||
|
import org.hl7.fhir.r5.model.Patient;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class FhirResourceDaoR5HistoryDisabledTest extends BaseJpaR5Test {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void beforeEach() {
|
||||||
|
myStorageSettings.setResourceDbHistoryEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void afterEach() {
|
||||||
|
JpaStorageSettings defaults = new JpaStorageSettings();
|
||||||
|
myStorageSettings.setResourceDbHistoryEnabled(defaults.isResourceDbHistoryEnabled());
|
||||||
|
myStorageSettings.setTagStorageMode(defaults.getTagStorageMode());
|
||||||
|
myStorageSettings.setStoreMetaSourceInformation(defaults.getStoreMetaSourceInformation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatch() {
|
||||||
|
// Setup
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
Parameters.ParametersParameterComponent op = patch.addParameter().setName("operation");
|
||||||
|
op.addPart().setName("type").setValue(new CodeType("replace"));
|
||||||
|
op.addPart().setName("path").setValue(new CodeType("Patient.active"));
|
||||||
|
op.addPart().setName("value").setValue(new BooleanType(false));
|
||||||
|
IIdType id2 = myPatientDao.patch(id1, null, PatchTypeEnum.FHIR_PATCH_JSON, null, patch, mySrd).getId();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
runInTransaction(() -> assertEquals(1, myResourceHistoryTableDao.count()));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2, mySrd));
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd));
|
||||||
|
assertThrows(ResourceNotFoundException.class, () -> myPatientDao.read(id2.withVersion("1"), mySrd));
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd);
|
||||||
|
assertFalse(p.getActive());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.withVersion("2"), mySrd);
|
||||||
|
assertFalse(p.getActive());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate() {
|
||||||
|
// Setup
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
IIdType id2 = myPatientDao.update(p, mySrd).getId();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
runInTransaction(() -> assertEquals(1, myResourceHistoryTableDao.count()));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2, mySrd));
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd));
|
||||||
|
assertThrows(ResourceNotFoundException.class, () -> myPatientDao.read(id2.withVersion("1"), mySrd));
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.withVersion("2"), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_InTransaction() {
|
||||||
|
// Setup
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
BundleBuilder bb = new BundleBuilder(myFhirContext);
|
||||||
|
bb.addTransactionUpdateEntry(p);
|
||||||
|
Bundle outcome = mySystemDao.transaction(mySrd, bb.getBundleTyped());
|
||||||
|
IIdType id2 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
runInTransaction(() -> assertEquals(1, myResourceHistoryTableDao.count()));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2, mySrd));
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd));
|
||||||
|
assertThrows(ResourceNotFoundException.class, () -> myPatientDao.read(id2.withVersion("1"), mySrd));
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.withVersion("2"), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_CurrentVersionWasExpunged() {
|
||||||
|
// Setup
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(() -> myResourceHistoryTableDao.deleteAll());
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
IIdType id2 = myPatientDao.update(p, mySrd).getId();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
runInTransaction(() -> assertEquals(1, myResourceHistoryTableDao.count()));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2, mySrd));
|
||||||
|
assertDoesNotThrow(() -> myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd));
|
||||||
|
assertThrows(ResourceNotFoundException.class, () -> myPatientDao.read(id2.withVersion("1"), mySrd));
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.toUnqualifiedVersionless(), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
|
||||||
|
p = myPatientDao.read(id2.withVersion("2"), mySrd);
|
||||||
|
assertEquals("foo", p.getIdentifier().get(0).getValue());
|
||||||
|
assertEquals("2", p.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", p.getMeta().getVersionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_VersionedTagsMode_TagsAreCarriedForward() {
|
||||||
|
// Setup
|
||||||
|
myStorageSettings.setTagStorageMode(JpaStorageSettings.TagStorageModeEnum.VERSIONED);
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar1");
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(2, myResourceTagDao.count());
|
||||||
|
assertEquals(2, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar3");
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
DaoMethodOutcome outcome = myPatientDao.update(p, mySrd);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertThat(toTagTokens(outcome.getResource()), containsInAnyOrder(
|
||||||
|
"http://foo|bar1", "http://foo|bar2", "http://foo|bar3"
|
||||||
|
));
|
||||||
|
|
||||||
|
p = myPatientDao.read(outcome.getId(), mySrd);
|
||||||
|
assertThat(toTagTokens(p), containsInAnyOrder(
|
||||||
|
"http://foo|bar1", "http://foo|bar2", "http://foo|bar3"
|
||||||
|
));
|
||||||
|
ourLog.info("Tag tokens: {}", toTagTokens(p));
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(3, myResourceTagDao.count());
|
||||||
|
assertEquals(3, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_VersionedTagsMode_TagsCanBeDeleted() {
|
||||||
|
// Setup
|
||||||
|
myStorageSettings.setTagStorageMode(JpaStorageSettings.TagStorageModeEnum.VERSIONED);
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar1");
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(2, myResourceTagDao.count());
|
||||||
|
assertEquals(2, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test
|
||||||
|
Meta meta = new Meta();
|
||||||
|
meta.addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
myPatientDao.metaDeleteOperation(id1.toVersionless(), meta, mySrd);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
p = myPatientDao.read(id1.toVersionless(), mySrd);
|
||||||
|
assertThat(toTagTokens(p), containsInAnyOrder(
|
||||||
|
"http://foo|bar1"
|
||||||
|
));
|
||||||
|
ourLog.info("Tag tokens: {}", toTagTokens(p));
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(1, myResourceTagDao.count());
|
||||||
|
assertEquals(1, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_NonVersionedTagsMode_TagsAreCarriedForward() {
|
||||||
|
// Setup
|
||||||
|
myStorageSettings.setTagStorageMode(JpaStorageSettings.TagStorageModeEnum.NON_VERSIONED);
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar1");
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(2, myResourceTagDao.count());
|
||||||
|
assertEquals(0, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar3");
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
DaoMethodOutcome outcome = myPatientDao.update(p, mySrd);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertThat(toTagTokens(outcome.getResource()), containsInAnyOrder(
|
||||||
|
"http://foo|bar1", "http://foo|bar2", "http://foo|bar3"
|
||||||
|
));
|
||||||
|
|
||||||
|
p = myPatientDao.read(outcome.getId(), mySrd);
|
||||||
|
assertThat(toTagTokens(p), containsInAnyOrder(
|
||||||
|
"http://foo|bar1", "http://foo|bar2", "http://foo|bar3"
|
||||||
|
));
|
||||||
|
ourLog.info("Tag tokens: {}", toTagTokens(p));
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(3, myResourceTagDao.count());
|
||||||
|
assertEquals(0, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_NonVersionedTagsMode_TagsCanBeDeleted() {
|
||||||
|
// Setup
|
||||||
|
myStorageSettings.setTagStorageMode(JpaStorageSettings.TagStorageModeEnum.NON_VERSIONED);
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar1");
|
||||||
|
p.getMeta().addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(2, myResourceTagDao.count());
|
||||||
|
assertEquals(0, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test
|
||||||
|
Meta meta = new Meta();
|
||||||
|
meta.addTag().setSystem("http://foo").setCode("bar2");
|
||||||
|
myPatientDao.metaDeleteOperation(id1.toVersionless(), meta, mySrd);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
p = myPatientDao.read(id1.toVersionless(), mySrd);
|
||||||
|
assertThat(toTagTokens(p), containsInAnyOrder(
|
||||||
|
"http://foo|bar1"
|
||||||
|
));
|
||||||
|
ourLog.info("Tag tokens: {}", toTagTokens(p));
|
||||||
|
runInTransaction(()-> {
|
||||||
|
assertEquals(1, myResourceTagDao.count());
|
||||||
|
assertEquals(0, myResourceHistoryTagDao.count());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdate_ProvenanceIsUpdatedInPlace() {
|
||||||
|
// Setup
|
||||||
|
myStorageSettings.setStoreMetaSourceInformation(JpaStorageSettings.StoreMetaSourceInformationEnum.SOURCE_URI_AND_REQUEST_ID);
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getMeta().setSource("source-1");
|
||||||
|
p.setActive(true);
|
||||||
|
when(mySrd.getRequestId()).thenReturn("request-id-1");
|
||||||
|
IIdType id1 = myPatientDao.create(p, mySrd).getId();
|
||||||
|
runInTransaction(()-> assertEquals(1, myResourceHistoryProvenanceDao.count()));
|
||||||
|
|
||||||
|
// Test
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id1);
|
||||||
|
p.addIdentifier().setValue("foo");
|
||||||
|
p.getMeta().setSource("source-2");
|
||||||
|
p.setActive(true);
|
||||||
|
when(mySrd.getRequestId()).thenReturn("request-id-2");
|
||||||
|
DaoMethodOutcome outcome = myPatientDao.update(p, mySrd);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals("source-2#request-id-2", ((Patient)outcome.getResource()).getMeta().getSource());
|
||||||
|
p = myPatientDao.read(outcome.getId(), mySrd);
|
||||||
|
assertEquals("source-2#request-id-2", p.getMeta().getSource());
|
||||||
|
runInTransaction(()-> assertEquals(1, myResourceHistoryProvenanceDao.count()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static List<String> toTagTokens(IBaseResource resource) {
|
||||||
|
List<String> tags = resource.getMeta()
|
||||||
|
.getTag()
|
||||||
|
.stream()
|
||||||
|
.map(t -> t.getSystem() + "|" + t.getCode())
|
||||||
|
.toList();
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -326,6 +326,10 @@ public class JpaStorageSettings extends StorageSettings {
|
||||||
* Applies to MDM links.
|
* Applies to MDM links.
|
||||||
*/
|
*/
|
||||||
private boolean myNonResourceDbHistoryEnabled = true;
|
private boolean myNonResourceDbHistoryEnabled = true;
|
||||||
|
/**
|
||||||
|
* Since 7.0.0
|
||||||
|
*/
|
||||||
|
private boolean myResourceHistoryDbEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -2302,9 +2306,50 @@ public class JpaStorageSettings extends StorageSettings {
|
||||||
myJobFastTrackingEnabled = theJobFastTrackingEnabled;
|
myJobFastTrackingEnabled = theJobFastTrackingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to {@literal false} (default is {@literal true}), the server will not
|
||||||
|
* preserve resource history and will delete previous versions of resources when
|
||||||
|
* a resource is updated.
|
||||||
|
* <p>
|
||||||
|
* Note that this does not make the server completely version-less. Resources will
|
||||||
|
* still have a version number which increases every time a resource is modified,
|
||||||
|
* operations such as vread and history will still be supported, and features
|
||||||
|
* such as ETags and ETag-aware updates will still work. Disabling this setting
|
||||||
|
* simply means that when a resource is updated, the previous version of the
|
||||||
|
* resource will be expunged. This could be done in order to conserve space, or
|
||||||
|
* in cases where there is no business value to storing previous versions of
|
||||||
|
* resources.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 7.0.0
|
||||||
|
*/
|
||||||
|
public boolean isResourceDbHistoryEnabled() {
|
||||||
|
return myResourceHistoryDbEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to {@literal false} (default is {@literal true}), the server will not
|
||||||
|
* preserve resource history and will delete previous versions of resources when
|
||||||
|
* a resource is updated.
|
||||||
|
* <p>
|
||||||
|
* Note that this does not make the server completely version-less. Resources will
|
||||||
|
* still have a version number which increases every time a resource is modified,
|
||||||
|
* operations such as vread and history will still be supported, and features
|
||||||
|
* such as ETags and ETag-aware updates will still work. Disabling this setting
|
||||||
|
* simply means that when a resource is updated, the previous version of the
|
||||||
|
* resource will be expunged. This could be done in order to conserve space, or
|
||||||
|
* in cases where there is no business value to storing previous versions of
|
||||||
|
* resources.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 7.0.0
|
||||||
|
*/
|
||||||
|
public void setResourceDbHistoryEnabled(boolean theResourceHistoryEnabled) {
|
||||||
|
myResourceHistoryDbEnabled = theResourceHistoryEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This setting controls whether MdmLink and other non-resource DB history is enabled.
|
* This setting controls whether MdmLink and other non-resource DB history is enabled.
|
||||||
* This setting controls whether non-resource DB history is enabled
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* By default, this is enabled unless explicitly disabled.
|
* By default, this is enabled unless explicitly disabled.
|
||||||
*
|
*
|
||||||
|
@ -2315,6 +2360,14 @@ public class JpaStorageSettings extends StorageSettings {
|
||||||
return myNonResourceDbHistoryEnabled;
|
return myNonResourceDbHistoryEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting controls whether MdmLink and other non-resource DB history is enabled.
|
||||||
|
* <p/>
|
||||||
|
* By default, this is enabled unless explicitly disabled.
|
||||||
|
*
|
||||||
|
* @param theNonResourceDbHistoryEnabled Whether non-resource DB history is enabled (default is true);
|
||||||
|
* @since 6.6.0
|
||||||
|
*/
|
||||||
public void setNonResourceDbHistoryEnabled(boolean theNonResourceDbHistoryEnabled) {
|
public void setNonResourceDbHistoryEnabled(boolean theNonResourceDbHistoryEnabled) {
|
||||||
myNonResourceDbHistoryEnabled = theNonResourceDbHistoryEnabled;
|
myNonResourceDbHistoryEnabled = theNonResourceDbHistoryEnabled;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue