issue-2901 added db access method for existence and setting the version to 1 if createplaceholders is true and object does not exist in db
This commit is contained in:
parent
e7c6ce920d
commit
a38b791141
|
@ -161,6 +161,15 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||||
*/
|
*/
|
||||||
T read(IIdType theId);
|
T read(IIdType theId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to determine if some resources exist in the DB (without throwing).
|
||||||
|
* Returns a set that contains the IIdType for every resource found.
|
||||||
|
* If it's not found, it won't be included in the set.
|
||||||
|
* @param theIds - list of IIdType ids (for the same resource)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Set<IIdType> hasResources(Collection<IIdType> theIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a resource by its internal PID
|
* Read a resource by its internal PID
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.api.model.LazyDaoMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
||||||
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
||||||
|
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||||
|
@ -1156,6 +1157,22 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return myTransactionService.execute(theRequest, transactionDetails, tx -> doRead(theId, theRequest, theDeletedOk));
|
return myTransactionService.execute(theRequest, transactionDetails, tx -> doRead(theId, theRequest, theDeletedOk));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<IIdType> hasResources(Collection<IIdType> theIds) {
|
||||||
|
List<String> idPortions = theIds.stream().map(t -> t.getIdPart()).collect(Collectors.toList());
|
||||||
|
Collection<Object[]> matches = myForcedIdDao.findResourcesByForcedId(getResourceName(),
|
||||||
|
idPortions);
|
||||||
|
|
||||||
|
HashSet<IIdType> collected = new HashSet<>();
|
||||||
|
for (Object[] match : matches) {
|
||||||
|
String resourceType = (String) match[0];
|
||||||
|
String forcedId = (String) match[1];
|
||||||
|
collected.add(new IdDt(resourceType, forcedId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return collected;
|
||||||
|
}
|
||||||
|
|
||||||
public T doRead(IIdType theId, RequestDetails theRequest, boolean theDeletedOk) {
|
public T doRead(IIdType theId, RequestDetails theRequest, boolean theDeletedOk) {
|
||||||
assert TransactionSynchronizationManager.isActualTransactionActive();
|
assert TransactionSynchronizationManager.isActualTransactionActive();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
|
@ -650,6 +650,7 @@ public abstract class BaseTransactionProcessor {
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
//FIXME - remove
|
//FIXME - remove
|
||||||
ourLog.info("FindME");
|
ourLog.info("FindME");
|
||||||
|
|
||||||
ourLog.info(ex.getLocalizedMessage());
|
ourLog.info(ex.getLocalizedMessage());
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
entriesToProcess = new HashMap<>();
|
entriesToProcess = new HashMap<>();
|
||||||
|
@ -1306,28 +1307,61 @@ public abstract class BaseTransactionProcessor {
|
||||||
} else if (nextId.getValue().startsWith("urn:")) {
|
} else if (nextId.getValue().startsWith("urn:")) {
|
||||||
throw new InvalidRequestException("Unable to satisfy placeholder ID " + nextId.getValue() + " found in element named '" + nextRef.getName() + "' within resource of type: " + nextResource.getIdElement().getResourceType());
|
throw new InvalidRequestException("Unable to satisfy placeholder ID " + nextId.getValue() + " found in element named '" + nextRef.getName() + "' within resource of type: " + nextResource.getIdElement().getResourceType());
|
||||||
} else {
|
} else {
|
||||||
if (theReferencesToAutoVersion.contains(resourceReference)) {
|
// resource type -> set of Ids
|
||||||
|
// we'll populate this with only those resource/ids of
|
||||||
|
// resource references that:
|
||||||
|
// a) do not exist in the idToPersistedOutcome
|
||||||
|
// (so not in top level of bundle)
|
||||||
|
// b) do not exist in DB
|
||||||
|
// (so newly created resources)
|
||||||
|
//
|
||||||
|
// we only do this if autocreateplaceholders is on
|
||||||
|
HashMap<String, Set<IIdType>> resourceTypeToListOfIds = new HashMap<>();
|
||||||
|
if (myDaoConfig.isAutoCreatePlaceholderReferenceTargets()) {
|
||||||
|
for (IBaseReference ref : theReferencesToAutoVersion) {
|
||||||
|
IIdType id = ref.getReferenceElement();
|
||||||
|
// if we don't have this in our idToPersistedOutcome
|
||||||
|
// and we have createplaceholderreferences on
|
||||||
|
// we will have to check if these objects exist in the DB
|
||||||
|
if (!theIdToPersistedOutcome.containsKey(id)) {
|
||||||
|
if (!resourceTypeToListOfIds.containsKey(id.getResourceType())) {
|
||||||
|
resourceTypeToListOfIds.put(id.getResourceType(), new HashSet<>());
|
||||||
|
}
|
||||||
|
|
||||||
//TODO - if we get here and there's still no
|
resourceTypeToListOfIds.get(id.getResourceType()).add(id);
|
||||||
// value in theIdToPersistedOutcome map
|
|
||||||
// we should throw an invalid request exception
|
|
||||||
|
|
||||||
DaoMethodOutcome outcome = theIdToPersistedOutcome.get(nextId);
|
|
||||||
|
|
||||||
if (outcome == null) {
|
|
||||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceReference.getReferenceElement().getResourceType());
|
|
||||||
IBaseResource baseResource;
|
|
||||||
try {
|
|
||||||
// DB hit
|
|
||||||
baseResource = dao.read(resourceReference.getReferenceElement());
|
|
||||||
} catch (ResourceNotFoundException ex) {
|
|
||||||
// does not exist - add the history/1
|
|
||||||
String val = resourceReference.getReferenceElement().getValue();
|
|
||||||
resourceReference.getReferenceElement().setValue(val + "/_history/1");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcome != null && !outcome.isNop() && !Boolean.TRUE.equals(outcome.getCreated())) {
|
for (String resourceType : resourceTypeToListOfIds.keySet()) {
|
||||||
|
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType);
|
||||||
|
Set<IIdType> idSet = resourceTypeToListOfIds.get(resourceType);
|
||||||
|
|
||||||
|
// DB hit :(
|
||||||
|
Set<IIdType> existing = dao.hasResources(idSet);
|
||||||
|
|
||||||
|
// we remove all the ids that are found, leaving
|
||||||
|
// only the ids of those not in the DB at all
|
||||||
|
idSet.removeAll(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theReferencesToAutoVersion.contains(resourceReference)) {
|
||||||
|
DaoMethodOutcome outcome = theIdToPersistedOutcome.get(nextId);
|
||||||
|
|
||||||
|
if (outcome == null && myDaoConfig.isAutoCreatePlaceholderReferenceTargets()) {
|
||||||
|
// null outcome means it's a resource reference that was not at top of bundle
|
||||||
|
IIdType id = resourceReference.getReferenceElement();
|
||||||
|
String resourceType = id.getResourceType();
|
||||||
|
// if it exists in resourceTypeToListOfIds
|
||||||
|
// it's not in the DB (new resource)
|
||||||
|
Set<IIdType> ids = resourceTypeToListOfIds.get(resourceType);
|
||||||
|
if (!ids.contains(id)) {
|
||||||
|
// doesn't exist in the DB
|
||||||
|
// which means the history necessarily is 1 (first one)
|
||||||
|
resourceReference.getReferenceElement().setValue(id.getValue() + "/_history/1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (outcome != null && !outcome.isNop() && !Boolean.TRUE.equals(outcome.getCreated())) {
|
||||||
addRollbackReferenceRestore(theTransactionDetails, resourceReference);
|
addRollbackReferenceRestore(theTransactionDetails, resourceReference);
|
||||||
resourceReference.setReference(nextId.getValue());
|
resourceReference.setReference(nextId.getValue());
|
||||||
resourceReference.setResource(null);
|
resourceReference.setResource(null);
|
||||||
|
|
|
@ -111,6 +111,13 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
||||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )")
|
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )")
|
||||||
Collection<Object[]> findAndResolveByForcedIdWithNoType(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
Collection<Object[]> findAndResolveByForcedIdWithNoType(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
||||||
|
|
||||||
|
@Query("" +
|
||||||
|
"SELECT " +
|
||||||
|
" f.myResourceType, f.myForcedId " +
|
||||||
|
"FROM ForcedId f " +
|
||||||
|
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )")
|
||||||
|
Collection<Object[]> findResourcesByForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||||
|
|
|
@ -301,8 +301,8 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertVersionedReferenceAtPathUsingTransaction() {
|
public void testInsertVersionedReferenceAtPathUsingTransaction() {
|
||||||
// myFhirCtx.getParserOptions().setStripVersionsFromReferences(false);
|
myFhirCtx.getParserOptions().setStripVersionsFromReferences(false);
|
||||||
// myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(true);
|
||||||
myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
|
myModelConfig.setAutoVersionReferenceAtPaths("Observation.subject");
|
||||||
|
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
|
@ -893,24 +893,21 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
||||||
BundleBuilder builder = new BundleBuilder(myFhirCtx);
|
BundleBuilder builder = new BundleBuilder(myFhirCtx);
|
||||||
builder.addTransactionUpdateEntry(obs);
|
builder.addTransactionUpdateEntry(obs);
|
||||||
|
|
||||||
|
Bundle submitted = (Bundle)builder.getBundle();
|
||||||
|
|
||||||
//1 make sure this test throws the InvalidRequestException (make separate test for this)
|
//1 make sure this test throws the InvalidRequestException (make separate test for this)
|
||||||
//2 add a test for patient created before bundle and then process observation with reference to patient (null check for outcome)
|
//2 add a test for patient created before bundle and then process observation with reference to patient (null check for outcome)
|
||||||
//3
|
//3
|
||||||
|
|
||||||
// Assertions.assertThrows()
|
Bundle returnedTr = mySystemDao.transaction(new SystemRequestDetails(), submitted);
|
||||||
try {
|
|
||||||
Bundle returnedTr = mySystemDao.transaction(new SystemRequestDetails(), (Bundle) builder.getBundle());
|
|
||||||
|
|
||||||
System.out.println("returned " + returnedTr.getEntry().size());
|
Assertions.assertTrue(returnedTr != null);
|
||||||
Observation obRet = myObservationDao.read(obs.getIdElement());
|
|
||||||
System.out.println("HELLO " + obRet.getId());
|
// some verification
|
||||||
Patient returned = myPatientDao.read(patientRef.getReferenceElement());
|
Observation obRet = myObservationDao.read(obs.getIdElement());
|
||||||
Assertions.assertTrue(returned != null);
|
Assertions.assertTrue(obRet != null);
|
||||||
}
|
Patient returned = myPatientDao.read(patientRef.getReferenceElement());
|
||||||
catch (Exception ex) {
|
Assertions.assertTrue(returned != null);
|
||||||
System.out.println("TEST " + ex.getLocalizedMessage());
|
|
||||||
Assertions.assertTrue(ex == null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue