Add Basic provider method, skeleton of tests
This commit is contained in:
parent
6259eada3c
commit
25d8668f55
|
@ -27,8 +27,9 @@ import ca.uhn.fhir.empi.model.EmpiTransactionContext;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
|
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.EmpiLink;
|
import ca.uhn.fhir.jpa.entity.EmpiLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.EmpiTargetType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Example;
|
import org.springframework.data.domain.Example;
|
||||||
|
@ -42,6 +43,7 @@ import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class EmpiLinkDaoSvc {
|
public class EmpiLinkDaoSvc {
|
||||||
|
@ -183,6 +185,26 @@ public class EmpiLinkDaoSvc {
|
||||||
return myEmpiLinkDao.findAll(example);
|
return myEmpiLinkDao.findAll(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Long> deleteAllEmpiLinksAndReturnPersonPids() {
|
||||||
|
List<EmpiLink> all = myEmpiLinkDao.findAll();
|
||||||
|
return deleteEmpiLinksAndReturnPersonPids(all);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Long> deleteEmpiLinksAndReturnPersonPids(List<EmpiLink> theLinks) {
|
||||||
|
List<Long> collect = theLinks.stream().map(link -> link.getPersonPid()).collect(Collectors.toList());
|
||||||
|
myEmpiLinkDao.deleteAll();
|
||||||
|
return collect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Long> deleteAllEmpiLinksOfTypeAndReturnPersonPids(EmpiTargetType theTargetType) {
|
||||||
|
EmpiLink link = new EmpiLink();
|
||||||
|
link.setEmpiTargetType(theTargetType);
|
||||||
|
Example<EmpiLink> exampleLink = Example.of(link);
|
||||||
|
List<EmpiLink> allOfType = myEmpiLinkDao.findAll(exampleLink);
|
||||||
|
return deleteEmpiLinksAndReturnPersonPids(allOfType);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public EmpiLink save(EmpiLink theEmpiLink) {
|
public EmpiLink save(EmpiLink theEmpiLink) {
|
||||||
if (theEmpiLink.getCreated() == null) {
|
if (theEmpiLink.getCreated() == null) {
|
||||||
theEmpiLink.setCreated(new Date());
|
theEmpiLink.setCreated(new Date());
|
||||||
|
@ -195,7 +217,7 @@ public class EmpiLinkDaoSvc {
|
||||||
return myEmpiLinkDao.findAll(theExampleLink);
|
return myEmpiLinkDao.findAll(theExampleLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EmpiLink> findEmpiLinksByTarget(Patient theTargetResource) {
|
public List<EmpiLink> findEmpiLinksByTarget(IAnyResource theTargetResource) {
|
||||||
Long pid = myIdHelperService.getPidOrNull(theTargetResource);
|
Long pid = myIdHelperService.getPidOrNull(theTargetResource);
|
||||||
if (pid == null) {
|
if (pid == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -37,13 +37,10 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.google.common.collect.MultimapBuilder;
|
import com.google.common.collect.MultimapBuilder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -55,7 +52,6 @@ import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -66,7 +62,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -481,7 +476,7 @@ public class IdHelperService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Long, IIdType> getPidToIdMap(Collection<IIdType> theIds, RequestDetails theRequestDetails) {
|
public Map<Long, IIdType> getPidToIdMap(Collection<IIdType> theIds, RequestDetails theRequestDetails) {
|
||||||
return theIds.stream().collect(Collectors.toMap(t->getPidOrThrowException(t), Function.identity()));
|
return theIds.stream().collect(Collectors.toMap(this::getPidOrThrowException, Function.identity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IIdType resourceIdFromPidOrThrowException(Long thePid) {
|
public IIdType resourceIdFromPidOrThrowException(Long thePid) {
|
||||||
|
|
|
@ -88,6 +88,12 @@ public class EmpiLink {
|
||||||
@Column(name = "UPDATED", nullable = false)
|
@Column(name = "UPDATED", nullable = false)
|
||||||
private Date myUpdated;
|
private Date myUpdated;
|
||||||
|
|
||||||
|
@Column(name = "TARGET_TYPE", nullable = true)
|
||||||
|
private EmpiTargetType myEmpiTargetType;
|
||||||
|
|
||||||
|
@Column(name = "SCORE", nullable = true)
|
||||||
|
private Double myScore;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
}
|
}
|
||||||
|
@ -205,4 +211,20 @@ public class EmpiLink {
|
||||||
myUpdated = theUpdated;
|
myUpdated = theUpdated;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EmpiTargetType getEmpiTargetType() {
|
||||||
|
return myEmpiTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmpiTargetType(EmpiTargetType theEmpiTargetType) {
|
||||||
|
myEmpiTargetType = theEmpiTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getScore() {
|
||||||
|
return myScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScore(Double theScore) {
|
||||||
|
myScore = theScore;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
public enum EmpiTargetType {
|
||||||
|
PATIENT,
|
||||||
|
PRACTITIONER
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.empi.config;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
||||||
|
@ -40,6 +41,7 @@ import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiCandidateSearchCriteriaBuilderSvc;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiCandidateSearchCriteriaBuilderSvc;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiCandidateSearchSvc;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiCandidateSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiEidUpdateService;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiEidUpdateService;
|
||||||
|
import ca.uhn.fhir.jpa.empi.svc.EmpiExpungeSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkQuerySvcImpl;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkQuerySvcImpl;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl;
|
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl;
|
||||||
|
@ -153,6 +155,11 @@ public class EmpiConsumerConfig {
|
||||||
return new EmpiLinkQuerySvcImpl();
|
return new EmpiLinkQuerySvcImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
IEmpiExpungeSvc empiExpungeSvc() {
|
||||||
|
return new EmpiExpungeSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
EmpiCandidateSearchSvc empiCandidateSearchSvc() {
|
EmpiCandidateSearchSvc empiCandidateSearchSvc() {
|
||||||
return new EmpiCandidateSearchSvc();
|
return new EmpiCandidateSearchSvc();
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package ca.uhn.fhir.jpa.empi.svc;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server - Enterprise Master Patient Index
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
|
import ca.uhn.fhir.jpa.dao.EmpiLinkDaoSvc;
|
||||||
|
import ca.uhn.fhir.jpa.dao.expunge.IResourceExpungeService;
|
||||||
|
import ca.uhn.fhir.jpa.entity.EmpiTargetType;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is in charge of Clearing out existing EMPI links, as well as deleting all persons related to those EMPI Links.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class EmpiExpungeSvcImpl implements IEmpiExpungeSvc {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(EmpiExpungeSvcImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IResourceExpungeService myResourceExpungeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void expungeEmpiLinks(String theResourceType) {
|
||||||
|
EmpiTargetType targetType = getTargetTypeOrThrowException(theResourceType);
|
||||||
|
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksOfTypeAndReturnPersonPids(targetType);
|
||||||
|
myResourceExpungeService.expungeHistoricalVersionsOfIds(null, longs, new AtomicInteger(longs.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmpiTargetType getTargetTypeOrThrowException(String theResourceType) {
|
||||||
|
if (theResourceType.equalsIgnoreCase("Patient")) {
|
||||||
|
return EmpiTargetType.PATIENT;
|
||||||
|
} else if(theResourceType.equalsIgnoreCase("Practitioner")) {
|
||||||
|
return EmpiTargetType.PRACTITIONER;
|
||||||
|
} else {
|
||||||
|
throw new InvalidRequestException(ProviderConstants.EMPI_CLEAR + " does not support resource type: " + theResourceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void expungeEmpiLinks() {
|
||||||
|
List<Long> longs = myEmpiLinkDaoSvc.deleteAllEmpiLinksAndReturnPersonPids();
|
||||||
|
myResourceExpungeService.expungeHistoricalVersionsOfIds(null, longs, new AtomicInteger(longs.size()));;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.empi.batch;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class EmpiBatchTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEmpiBatch() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,8 +50,10 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
|
||||||
return myEmpiLinkDaoSvc.findEmpiLinkByTarget(myPatient).get();
|
return myEmpiLinkDaoSvc.findEmpiLinkByTarget(myPatient).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected List<EmpiLink> getPatientLinks() {
|
protected List<EmpiLink> getPatientLinks() {
|
||||||
return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPatient);
|
return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPatient);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.empi.provider;
|
package ca.uhn.fhir.jpa.empi.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||||
|
@ -21,11 +22,13 @@ public abstract class BaseProviderR4Test extends BaseEmpiR4Test {
|
||||||
private IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
private IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceLoader myResourceLoader;
|
private IResourceLoader myResourceLoader;
|
||||||
|
@Autowired
|
||||||
|
private IEmpiExpungeSvc myEmpiExpungeSvc;
|
||||||
|
|
||||||
EmpiProviderR4 myEmpiProviderR4;
|
EmpiProviderR4 myEmpiProviderR4;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
myEmpiProviderR4 = new EmpiProviderR4(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader);
|
myEmpiProviderR4 = new EmpiProviderR4(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader, myEmpiExpungeSvc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
package ca.uhn.fhir.jpa.empi.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.empi.api.EmpiConstants;
|
||||||
|
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
|
||||||
|
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.jpa.entity.EmpiLink;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.Person;
|
||||||
|
import org.hl7.fhir.r4.model.Practitioner;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.matchesPattern;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
|
||||||
|
|
||||||
|
|
||||||
|
protected Practitioner myPractitioner;
|
||||||
|
protected StringType myPractitionerId;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
myPractitioner = createPractitionerAndUpdateLinks(new Practitioner());
|
||||||
|
myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearAllLinks() {
|
||||||
|
assertLinkCount(2);
|
||||||
|
myEmpiProviderR4.clearEmpiLinks(null);
|
||||||
|
assertLinkCount(0);
|
||||||
|
assertNoLinksExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoLinksExist() {
|
||||||
|
assertNoPatientLinksExist();
|
||||||
|
assertNoPractitionerLinksExist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoPatientLinksExist() {
|
||||||
|
assertThat(getPatientLinks(), hasSize(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoPractitionerLinksExist() {
|
||||||
|
assertThat(getPractitionerLinks(), hasSize(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearPatientLinks() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
List<EmpiLink> links = getPatientLinks();
|
||||||
|
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(EmpiMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearPractitionerLinks() {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (ResourceVersionConflictException e) {
|
||||||
|
assertThat(e.getMessage(), matchesPattern("Requested resource Person/\\d+/_history/1 is not the latest version. Latest version is Person/\\d+/_history/2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClearInvalidTargetType() {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
Person person = myEmpiProviderR4.updateLink(myVersionlessPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
assertThat(person.getLink(), hasSize(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnlinkLink() {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (ResourceVersionConflictException e) {
|
||||||
|
assertThat(e.getMessage(), matchesPattern("Requested resource Person/\\d+/_history/1 is not the latest version. Latest version is Person/\\d+/_history/2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateIllegalResultPM() {
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("$empi-update-link illegal matchResult value 'POSSIBLE_MATCH'. Must be NO_MATCH or MATCH", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateIllegalResultPD() {
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("$empi-update-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH or MATCH", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateIllegalFirstArg() {
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("personId must have form Person/<id> where <id> is the id of the person", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateIllegalSecondArg() {
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, myPersonId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("must have form Patient/<id> or Practitioner/<id> where <id> is the id of the resource"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateStrangePerson() {
|
||||||
|
Person person = createUnmanagedPerson();
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(new StringType(person.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("Only EMPI Managed Person resources may be updated via this operation. The Person resource provided is not tagged as managed by hapi-empi", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExcludedPerson() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_EMPI_MANAGED).setCode(EmpiConstants.CODE_NO_EMPI_MANAGED);
|
||||||
|
createPatient(patient);
|
||||||
|
try {
|
||||||
|
myEmpiProviderR4.updateLink(myPersonId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("The target is marked with the " + EmpiConstants.CODE_NO_EMPI_MANAGED + " tag which means it may not be EMPI linked.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected EmpiLink getOnlyPractitionerLink() {
|
||||||
|
return myEmpiLinkDaoSvc.findEmpiLinkByTarget(myPractitioner).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected List<EmpiLink> getPractitionerLinks() {
|
||||||
|
return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPractitioner);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package ca.uhn.fhir.empi.api;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Enterprise Master Patient Index
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface IEmpiExpungeSvc {
|
||||||
|
|
||||||
|
void expungeEmpiLinks(String theResourceType);
|
||||||
|
|
||||||
|
void expungeEmpiLinks();
|
||||||
|
}
|
|
@ -28,4 +28,5 @@ public interface IEmpiLinkQuerySvc {
|
||||||
IBaseParameters queryLinks(IIdType thePersonId, IIdType theTargetId, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, EmpiTransactionContext theEmpiContext);
|
IBaseParameters queryLinks(IIdType thePersonId, IIdType theTargetId, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, EmpiTransactionContext theEmpiContext);
|
||||||
|
|
||||||
IBaseParameters getPossibleDuplicates(EmpiTransactionContext theEmpiContext);
|
IBaseParameters getPossibleDuplicates(EmpiTransactionContext theEmpiContext);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.empi.provider;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
|
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
|
||||||
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||||
|
@ -52,6 +53,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
|
||||||
private final IEmpiPersonMergerSvc myPersonMergerSvc;
|
private final IEmpiPersonMergerSvc myPersonMergerSvc;
|
||||||
private final IEmpiLinkUpdaterSvc myEmpiLinkUpdaterSvc;
|
private final IEmpiLinkUpdaterSvc myEmpiLinkUpdaterSvc;
|
||||||
private final IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
private final IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
||||||
|
private final IEmpiExpungeSvc myEmpiExpungeSvc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -59,12 +61,13 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
|
||||||
* Note that this is not a spring bean. Any necessary injections should
|
* Note that this is not a spring bean. Any necessary injections should
|
||||||
* happen in the constructor
|
* happen in the constructor
|
||||||
*/
|
*/
|
||||||
public EmpiProviderDstu3(FhirContext theFhirContext, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiPersonMergerSvc thePersonMergerSvc, IEmpiLinkUpdaterSvc theEmpiLinkUpdaterSvc, IEmpiLinkQuerySvc theEmpiLinkQuerySvc, IResourceLoader theResourceLoader) {
|
public EmpiProviderDstu3(FhirContext theFhirContext, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiPersonMergerSvc thePersonMergerSvc, IEmpiLinkUpdaterSvc theEmpiLinkUpdaterSvc, IEmpiLinkQuerySvc theEmpiLinkQuerySvc, IResourceLoader theResourceLoader, IEmpiExpungeSvc theEmpiExpungeSvc) {
|
||||||
super(theFhirContext, theResourceLoader);
|
super(theFhirContext, theResourceLoader);
|
||||||
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
|
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
|
||||||
myPersonMergerSvc = thePersonMergerSvc;
|
myPersonMergerSvc = thePersonMergerSvc;
|
||||||
myEmpiLinkUpdaterSvc = theEmpiLinkUpdaterSvc;
|
myEmpiLinkUpdaterSvc = theEmpiLinkUpdaterSvc;
|
||||||
myEmpiLinkQuerySvc = theEmpiLinkQuerySvc;
|
myEmpiLinkQuerySvc = theEmpiLinkQuerySvc;
|
||||||
|
myEmpiExpungeSvc = theEmpiExpungeSvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
|
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.empi.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||||
|
@ -47,14 +48,16 @@ public class EmpiProviderLoader {
|
||||||
private IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
private IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceLoader myResourceLoader;
|
private IResourceLoader myResourceLoader;
|
||||||
|
@Autowired
|
||||||
|
private IEmpiExpungeSvc myEmpiExpungeSvc;
|
||||||
|
|
||||||
public void loadProvider() {
|
public void loadProvider() {
|
||||||
switch (myFhirContext.getVersion().getVersion()) {
|
switch (myFhirContext.getVersion().getVersion()) {
|
||||||
case DSTU3:
|
case DSTU3:
|
||||||
myResourceProviderFactory.addSupplier(() -> new EmpiProviderDstu3(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader));
|
myResourceProviderFactory.addSupplier(() -> new EmpiProviderDstu3(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader, myEmpiExpungeSvc));
|
||||||
break;
|
break;
|
||||||
case R4:
|
case R4:
|
||||||
myResourceProviderFactory.addSupplier(() -> new EmpiProviderR4(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader));
|
myResourceProviderFactory.addSupplier(() -> new EmpiProviderR4(myFhirContext, myEmpiMatchFinderSvc, myPersonMergerSvc, myEmpiLinkUpdaterSvc, myEmpiLinkQuerySvc, myResourceLoader, myEmpiExpungeSvc));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ConfigurationException("EMPI not supported for FHIR version " + myFhirContext.getVersion().getVersion());
|
throw new ConfigurationException("EMPI not supported for FHIR version " + myFhirContext.getVersion().getVersion());
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.empi.provider;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
|
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
|
||||||
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
|
||||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||||
|
@ -52,6 +53,7 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
|
||||||
private final IEmpiPersonMergerSvc myPersonMergerSvc;
|
private final IEmpiPersonMergerSvc myPersonMergerSvc;
|
||||||
private final IEmpiLinkUpdaterSvc myEmpiLinkUpdaterSvc;
|
private final IEmpiLinkUpdaterSvc myEmpiLinkUpdaterSvc;
|
||||||
private final IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
private final IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
|
||||||
|
private final IEmpiExpungeSvc myEmpiExpungeSvc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -59,12 +61,13 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
|
||||||
* Note that this is not a spring bean. Any necessary injections should
|
* Note that this is not a spring bean. Any necessary injections should
|
||||||
* happen in the constructor
|
* happen in the constructor
|
||||||
*/
|
*/
|
||||||
public EmpiProviderR4(FhirContext theFhirContext, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiPersonMergerSvc thePersonMergerSvc, IEmpiLinkUpdaterSvc theEmpiLinkUpdaterSvc, IEmpiLinkQuerySvc theEmpiLinkQuerySvc, IResourceLoader theResourceLoader) {
|
public EmpiProviderR4(FhirContext theFhirContext, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiPersonMergerSvc thePersonMergerSvc, IEmpiLinkUpdaterSvc theEmpiLinkUpdaterSvc, IEmpiLinkQuerySvc theEmpiLinkQuerySvc, IResourceLoader theResourceLoader, IEmpiExpungeSvc theEmpiExpungeSvc) {
|
||||||
super(theFhirContext, theResourceLoader);
|
super(theFhirContext, theResourceLoader);
|
||||||
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
|
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
|
||||||
myPersonMergerSvc = thePersonMergerSvc;
|
myPersonMergerSvc = thePersonMergerSvc;
|
||||||
myEmpiLinkUpdaterSvc = theEmpiLinkUpdaterSvc;
|
myEmpiLinkUpdaterSvc = theEmpiLinkUpdaterSvc;
|
||||||
myEmpiLinkQuerySvc = theEmpiLinkQuerySvc;
|
myEmpiLinkQuerySvc = theEmpiLinkQuerySvc;
|
||||||
|
myEmpiExpungeSvc = theEmpiExpungeSvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
|
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
|
||||||
|
@ -117,6 +120,16 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
|
||||||
return (Person) myEmpiLinkUpdaterSvc.updateLink(person, target, matchResult, createEmpiContext(theRequestDetails));
|
return (Person) myEmpiLinkUpdaterSvc.updateLink(person, target, matchResult, createEmpiContext(theRequestDetails));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(name = ProviderConstants.EMPI_CLEAR)
|
||||||
|
public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.EMPI_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType) {
|
||||||
|
if (theTargetType == null) {
|
||||||
|
myEmpiExpungeSvc.expungeEmpiLinks();
|
||||||
|
} else {
|
||||||
|
myEmpiExpungeSvc.expungeEmpiLinks(theTargetType.getValueNotNull());
|
||||||
|
}
|
||||||
|
return new Parameters();
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.EMPI_QUERY_LINKS, idempotent = true)
|
@Operation(name = ProviderConstants.EMPI_QUERY_LINKS, idempotent = true)
|
||||||
public Parameters queryLinks(@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_PERSON_ID, min = 0, max = 1) StringType thePersonId,
|
public Parameters queryLinks(@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_PERSON_ID, min = 0, max = 1) StringType thePersonId,
|
||||||
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_TARGET_ID, min = 0, max = 1) StringType theTargetId,
|
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_TARGET_ID, min = 0, max = 1) StringType theTargetId,
|
||||||
|
|
|
@ -81,4 +81,7 @@ public class ProviderConstants {
|
||||||
|
|
||||||
public static final String EMPI_DUPLICATE_PERSONS = "$empi-duplicate-persons";
|
public static final String EMPI_DUPLICATE_PERSONS = "$empi-duplicate-persons";
|
||||||
public static final String EMPI_NOT_DUPLICATE = "$empi-not-duplicate";
|
public static final String EMPI_NOT_DUPLICATE = "$empi-not-duplicate";
|
||||||
|
|
||||||
|
public static final String EMPI_CLEAR = "$empi-clear";
|
||||||
|
public static final String EMPI_CLEAR_TARGET_TYPE = "targetType";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue