Fixed search for resources for $everything operation with partitions and MDM (#5504)
* Fixed search for resources for $everything operation with partitions and client id mode = ANY * fixed formatting * changelog file renamed for tests * code review fixes: moved generics mocks to class fields, changed size > 0 to empty check * removed unused fields * moved fields back to methods, changed generic anys * changed imports from javax to jakarta * code review fixes
This commit is contained in:
parent
9027138962
commit
da9be29047
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
jira: SMILE-7624
|
||||
title: "Previously, it was impossible to find all resources from different partitions for $everything operation
|
||||
with partitioning.cross_partition_reference_mode=ALLOWED_UNQUALIFIED and dao_config.client_id_mode=ANY.
|
||||
It's fixed now"
|
|
@ -210,7 +210,9 @@ public class IdHelperService implements IIdHelperService<JpaPid> {
|
|||
Validate.isTrue(!theIds.isEmpty(), "theIds must not be empty");
|
||||
|
||||
Map<String, JpaPid> retVals = new HashMap<>();
|
||||
|
||||
RequestPartitionId partitionId = myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()
|
||||
? RequestPartitionId.allPartitions()
|
||||
: theRequestPartitionId;
|
||||
for (String id : theIds) {
|
||||
JpaPid retVal;
|
||||
if (!idRequiresForcedId(id)) {
|
||||
|
@ -221,18 +223,17 @@ public class IdHelperService implements IIdHelperService<JpaPid> {
|
|||
// is a forced id
|
||||
// we must resolve!
|
||||
if (myStorageSettings.isDeleteEnabled()) {
|
||||
retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, id, theExcludeDeleted)
|
||||
retVal = resolveResourceIdentity(partitionId, theResourceType, id, theExcludeDeleted)
|
||||
.getPersistentId();
|
||||
retVals.put(id, retVal);
|
||||
} else {
|
||||
// fetch from cache... adding to cache if not available
|
||||
String key = toForcedIdToPidKey(theRequestPartitionId, theResourceType, id);
|
||||
String key = toForcedIdToPidKey(partitionId, theResourceType, id);
|
||||
retVal = myMemoryCacheService.getThenPutAfterCommit(
|
||||
MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, t -> {
|
||||
List<IIdType> ids = Collections.singletonList(new IdType(theResourceType, id));
|
||||
// fetches from cache using a function that checks cache first...
|
||||
List<JpaPid> resolvedIds =
|
||||
resolveResourcePersistentIdsWithCache(theRequestPartitionId, ids);
|
||||
List<JpaPid> resolvedIds = resolveResourcePersistentIdsWithCache(partitionId, ids);
|
||||
if (resolvedIds.isEmpty()) {
|
||||
throw new ResourceNotFoundException(Msg.code(1100) + ids.get(0));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package ca.uhn.fhir.jpa.dao.index;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.*;
|
||||
import org.hl7.fhir.r4.hapi.ctx.FhirR4;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class IdHelperServiceTest {
|
||||
|
||||
@InjectMocks
|
||||
private final IdHelperService subject = new IdHelperService();
|
||||
|
||||
@Mock
|
||||
protected IResourceTableDao myResourceTableDao;
|
||||
|
||||
@Mock
|
||||
private JpaStorageSettings myStorageSettings;
|
||||
|
||||
@Mock
|
||||
private FhirContext myFhirCtx;
|
||||
|
||||
@Mock
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
|
||||
@Mock
|
||||
private EntityManager myEntityManager;
|
||||
|
||||
@Mock
|
||||
private PartitionSettings myPartitionSettings;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
subject.setDontCheckActiveTransactionForUnitTest(true);
|
||||
|
||||
when(myStorageSettings.isDeleteEnabled()).thenReturn(true);
|
||||
when(myStorageSettings.getResourceClientIdStrategy()).thenReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY);
|
||||
when(myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveResourcePersistentIds() {
|
||||
//prepare params
|
||||
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionName("Partition-A");
|
||||
String resourceType = "Patient";
|
||||
Long id = 123L;
|
||||
List<String> ids = List.of(String.valueOf(id));
|
||||
boolean theExcludeDeleted = false;
|
||||
|
||||
//prepare results
|
||||
Patient expectedPatient = new Patient();
|
||||
expectedPatient.setId(ids.get(0));
|
||||
Object[] obj = new Object[] {resourceType, Long.parseLong(ids.get(0)), ids.get(0), Date.from(Instant.now())};
|
||||
|
||||
// configure mock behaviour
|
||||
when(myStorageSettings.isDeleteEnabled()).thenReturn(true);
|
||||
when(myResourceTableDao
|
||||
.findAndResolveByForcedIdWithNoType(eq(resourceType), eq(ids), eq(theExcludeDeleted)))
|
||||
.thenReturn(Collections.singletonList(obj));
|
||||
|
||||
Map<String, JpaPid> actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted);
|
||||
|
||||
//verify results
|
||||
assertFalse(actualIds.isEmpty());
|
||||
assertEquals(id, actualIds.get(ids.get(0)).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveResourcePersistentIdsDeleteFalse() {
|
||||
//prepare Params
|
||||
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionName("Partition-A");
|
||||
Long id = 123L;
|
||||
String resourceType = "Patient";
|
||||
List<String> ids = List.of(String.valueOf(id));
|
||||
String forcedId = "(all)/" + resourceType + "/" + id;
|
||||
boolean theExcludeDeleted = false;
|
||||
|
||||
//prepare results
|
||||
Patient expectedPatient = new Patient();
|
||||
expectedPatient.setId(ids.get(0));
|
||||
|
||||
// configure mock behaviour
|
||||
configureCacheBehaviour(forcedId);
|
||||
configureEntityManagerBehaviour(id, resourceType, ids.get(0));
|
||||
when(myStorageSettings.isDeleteEnabled()).thenReturn(false);
|
||||
when(myFhirCtx.getVersion()).thenReturn(new FhirR4());
|
||||
|
||||
Map<String, JpaPid> actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted);
|
||||
|
||||
//verifyResult
|
||||
assertFalse(actualIds.isEmpty());
|
||||
assertEquals(id, actualIds.get(ids.get(0)).getId());
|
||||
}
|
||||
|
||||
private void configureCacheBehaviour(String resourceUrl) {
|
||||
when(myMemoryCacheService.getThenPutAfterCommit(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl), any())).thenCallRealMethod();
|
||||
doNothing().when(myMemoryCacheService).putAfterCommit(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl), ArgumentMatchers.<JpaPid>any());
|
||||
when(myMemoryCacheService.getIfPresent(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl))).thenReturn(null);
|
||||
}
|
||||
|
||||
private void configureEntityManagerBehaviour(Long idNumber, String resourceType, String id) {
|
||||
List<Tuple> mockedTupleList = getMockedTupleList(idNumber, resourceType, id);
|
||||
CriteriaBuilder builder = getMockedCriteriaBuilder();
|
||||
Root<ResourceTable> from = getMockedFrom();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TypedQuery<Tuple> query = (TypedQuery<Tuple>) mock(TypedQuery.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
CriteriaQuery<Tuple> cq = mock(CriteriaQuery.class);
|
||||
|
||||
when(builder.createTupleQuery()).thenReturn(cq);
|
||||
when(cq.from(ArgumentMatchers.<Class<ResourceTable>>any())).thenReturn(from);
|
||||
when(query.getResultList()).thenReturn(mockedTupleList);
|
||||
|
||||
when(myEntityManager.getCriteriaBuilder()).thenReturn(builder);
|
||||
when(myEntityManager.createQuery(ArgumentMatchers.<CriteriaQuery<Tuple>>any())).thenReturn(query);
|
||||
}
|
||||
|
||||
private CriteriaBuilder getMockedCriteriaBuilder() {
|
||||
Predicate pred = mock(Predicate.class);
|
||||
CriteriaBuilder builder = mock(CriteriaBuilder.class);
|
||||
lenient().when(builder.equal(any(), any())).thenReturn(pred);
|
||||
return builder;
|
||||
}
|
||||
private Root<ResourceTable> getMockedFrom() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Path<Object> path = mock(Path.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Root<ResourceTable> from = mock(Root.class);
|
||||
lenient().when(from.get(ArgumentMatchers.<String>any())).thenReturn(path);
|
||||
return from;
|
||||
}
|
||||
|
||||
private List<Tuple> getMockedTupleList(Long idNumber, String resourceType, String id) {
|
||||
Tuple tuple = mock(Tuple.class);
|
||||
when(tuple.get(eq(0), eq(Long.class))).thenReturn(idNumber);
|
||||
when(tuple.get(eq(1), eq(String.class))).thenReturn(resourceType);
|
||||
when(tuple.get(eq(2), eq(String.class))).thenReturn(id);
|
||||
return List.of(tuple);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue