diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml index d1ed402450b..08691d1826a 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/changes.yaml @@ -25,8 +25,3 @@ has been replaced with an equivalent `FhirContext.newFhirPath()`. The FhirPath expression language was initially called FluentPath before being renamed, so this change brings HAPI FHIR inline with the correct naming. " -- item: - type: change - title: "The JPA server now shared a single set of tags for all versions of a resource, bringing it in line with the - functional description in the FHIR specification. This means that it is no longer possible to modify the tags for - a specific version of a resource, and also causes significant performance improvements in some cases." diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java index 33b975a8947..f62bc29557e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/MultitenantR4Test.java @@ -28,6 +28,7 @@ import org.junit.Test; import javax.servlet.ServletException; import java.time.LocalDate; import java.time.Month; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -36,16 +37,19 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.when; public class MultitenantR4Test extends BaseJpaR4SystemTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MultitenantR4Test.class); + private MyInterceptor myTenantInterceptor; @After public void after() { myDaoConfig.setMultiTenancyEnabled(new DaoConfig().isMultiTenancyEnabled()); myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyInterceptor); + myInterceptor = null; } @Override @@ -74,56 +78,84 @@ public class MultitenantR4Test extends BaseJpaR4SystemTest { public void testCreateResourceWithTenant() { createUniqueCompositeSp(); - int expectId = 3; - LocalDate expectDate = LocalDate.of(2020, Month.JANUARY, 14); - myInterceptorRegistry.registerInterceptor(new MyInterceptor(new TenantId(expectId, expectDate))); + addTenant(3, LocalDate.of(2020, Month.JANUARY, 14)); + addTenant(3, LocalDate.of(2020, Month.JANUARY, 14)); Organization org = new Organization(); org.setName("org"); IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); Patient p = new Patient(); + p.getMeta().addTag("http://system", "code", "diisplay"); p.addName().setFamily("FAM"); p.addIdentifier().setSystem("system").setValue("value"); p.setBirthDate(new Date()); p.getManagingOrganization().setReferenceElement(orgId); - Long patientId = myPatientDao.create(p).getId().getIdPartAsLong(); + when(mySrd.getRequestId()).thenReturn("REQUEST_ID"); + Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); runInTransaction(() -> { // HFJ_RESOURCE ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new); - assertEquals(expectId, resourceTable.getTenantId().getTenantId().intValue()); - assertEquals(expectDate, resourceTable.getTenantId().getTenantDate()); + assertEquals(3, resourceTable.getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), resourceTable.getTenantId().getTenantDate()); + + resourceTable.getProfile() // HFJ_RES_VER ResourceHistoryTable version = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(patientId, 1L); - assertEquals(expectId, version.getTenantId().getTenantId().intValue()); - assertEquals(expectDate, version.getTenantId().getTenantDate()); + assertEquals(3, version.getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), version.getTenantId().getTenantDate()); // HFJ_SPIDX_STRING List strings = myResourceIndexedSearchParamStringDao.findAllForResourceId(patientId); ourLog.info("\n * {}", strings.stream().map(ResourceIndexedSearchParamString::toString).collect(Collectors.joining("\n * "))); assertEquals(10, strings.size()); - assertEquals(expectId, strings.get(0).getTenantId().getTenantId().intValue()); - assertEquals(expectDate, strings.get(0).getTenantId().getTenantDate()); + assertEquals(3, strings.get(0).getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), strings.get(0).getTenantId().getTenantDate()); // HFJ_RES_LINK List resourceLinks = myResourceLinkDao.findAllForResourceId(patientId); assertEquals(1, resourceLinks.size()); - assertEquals(expectId, resourceLinks.get(0).getTenantId().getTenantId().intValue()); - assertEquals(expectDate, resourceLinks.get(0).getTenantId().getTenantDate()); + assertEquals(3, resourceLinks.get(0).getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), resourceLinks.get(0).getTenantId().getTenantDate()); // HFJ_RES_PARAM_PRESENT List presents = mySearchParamPresentDao.findAllForResource(resourceTable); assertEquals(3, presents.size()); - assertEquals(expectId, presents.get(0).getTenantId().getTenantId().intValue()); - assertEquals(expectDate, presents.get(0).getTenantId().getTenantDate()); + assertEquals(3, presents.get(0).getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), presents.get(0).getTenantId().getTenantDate()); // HFJ_IDX_CMP_STRING_UNIQ List uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceId(patientId); assertEquals(3, uniques.size()); - assertEquals(expectId, uniques.get(0).getTenantId().getTenantId().intValue()); - assertEquals(expectDate, uniques.get(0).getTenantId().getTenantDate()); + assertEquals(3, uniques.get(0).getTenantId().getTenantId().intValue()); + assertEquals(LocalDate.of(2020, Month.JANUARY, 14), uniques.get(0).getTenantId().getTenantDate()); + }); + + } + + @Test + public void testUpdateResourceWithTenant() { + createUniqueCompositeSp(); + + addTenant(3, LocalDate.of(2020, Month.JANUARY, 14)); + + Patient p = new Patient(); + p.setActive(true); + Long patientId = myPatientDao.create(p).getId().getIdPartAsLong(); + + p = new Patient(); + p.setId("Patient/" + patientId); + p.setActive(false); + myPatientDao.update(p); + + runInTransaction(() -> { + // HFJ_RES_VER + ResourceHistoryTable resVer = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(patientId, 2); + assertEquals(tenantId, resVer.getTenantId().getTenantId().intValue()); + assertEquals(tenantDate, resVer.getTenantId().getTenantDate()); + }); } @@ -154,19 +186,27 @@ public class MultitenantR4Test extends BaseJpaR4SystemTest { mySearchParamRegistry.forceRefresh(); } + public void addTenant(int theTenantId, LocalDate theTenantDate) { + if (myTenantInterceptor == null) { + myTenantInterceptor = new MyInterceptor(); + myInterceptorRegistry.registerInterceptor(myInterceptor); + } + myTenantInterceptor.addTenant(new TenantId(theTenantId, theTenantDate)); + } + @Interceptor public static class MyInterceptor { - private final List myTenantIds; + private final List myTenantIds = new ArrayList<>(); - public MyInterceptor(TenantId theTenantId) { + public void addTenant(TenantId theTenantId) { Validate.notNull(theTenantId); - myTenantIds = Collections.singletonList(theTenantId); + myTenantIds.add(theTenantId); } @Hook(Pointcut.STORAGE_TENANT_IDENTIFY_CREATE) public TenantId tenantIdentifyCreate() { - TenantId retVal = myTenantIds.get(0); + TenantId retVal = myTenantIds.remove(0); ourLog.info("Returning tenant ID: {}", retVal); return retVal; } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 31a71c7b1f1..057c28aaafd 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -57,10 +57,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { init400(); // 20190401 - 20190814 init410(); // 20190815 - 20191014 init420(); // 20191015 - 20200217 - init430(); // 20200218 - present + init500(); // 20200218 - present } - protected void init430() { // 20200218 - present + protected void init500() { // 20200218 - present Builder version = forVersion(VersionEnum.V4_3_0); // Eliminate circular dependency. @@ -68,6 +68,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RES_VER").dropColumn("20200218.2", "FORCED_ID_PID"); version.onTable("HFJ_RES_VER").addForeignKey("20200218.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID"); version.onTable("HFJ_RES_VER").modifyColumn("20200220.1", "RES_ID").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); + + // Add mlutiitenancy + version.onTable("HFJ_RESOURCE").dropColumn("20200327.1", "RES_PROFILE"); } protected void init420() { // 20191015 - 20200217 diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index 731227e8af4..0f1ffb8f769 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -60,7 +60,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString; public class ResourceTable extends BaseHasResource implements Serializable, IBasePersistedResource, IResourceLookup { public static final int RESTYPE_LEN = 40; private static final int MAX_LANGUAGE_LENGTH = 20; - private static final int MAX_PROFILE_LENGTH = 200; private static final long serialVersionUID = 1L; /** @@ -167,10 +166,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private boolean myParamsUriPopulated; - @Column(name = "RES_PROFILE", length = MAX_PROFILE_LENGTH, nullable = true) - @OptimisticLock(excluded = true) - private String myProfile; - // Added in 3.0.0 - Should make this a primitive Boolean at some point @OptimisticLock(excluded = true) @Column(name = "SP_CMPSTR_UNIQ_PRESENT") @@ -402,17 +397,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas getParamsUri().addAll(theParamsUri); } - public String getProfile() { - return myProfile; - } - - public void setProfile(String theProfile) { - if (defaultString(theProfile).length() > MAX_PROFILE_LENGTH) { - throw new UnprocessableEntityException("Profile name exceeds maximum length of " + MAX_PROFILE_LENGTH + " chars: " + theProfile); - } - myProfile = theProfile; - } - @Override public Long getResourceId() { return getId(); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java index 7d06439837c..a7e2cd1c950 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java @@ -50,6 +50,7 @@ public class ResourceTag extends BaseTag { @Column(name = "RES_ID", insertable = false, updatable = false) private Long myResourceId; + @Embedded private TenantId myTenantId;