Skip to content

Commit

Permalink
Allow server assigned UUIDs instead of sequential IDs if conigured to do
Browse files Browse the repository at this point in the history
so
  • Loading branch information
jamesagnew committed Dec 7, 2017
1 parent 0fb10b2 commit 36e719f
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 27 deletions.
Expand Up @@ -149,6 +149,10 @@ public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean the
}
}

if (myDaoConfig.getResourceIdStrategy() == DaoConfig.IdStrategyEnum.UUID) {
theResource.setId(UUID.randomUUID().toString());
}

return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date(), theRequestDetails);
}

Expand Down
Expand Up @@ -109,6 +109,7 @@ public class DaoConfig {
private boolean myAutoCreatePlaceholderReferenceTargets;
private Integer myCacheControlNoStoreMaxResultsUpperLimit = 1000;
private Integer myCountSearchResultsUpTo = null;
private IdStrategyEnum myResourceIdStrategy = IdStrategyEnum.SEQUENTIAL_NUMERIC;

/**
* Constructor
Expand Down Expand Up @@ -160,18 +161,18 @@ public void setCacheControlNoStoreMaxResultsUpperLimit(Integer theCacheControlNo
* whether a "total count" is included in the response bundle for searches that
* return large amounts of data.
* <p>
* For a search that returns 10000 results, if this value is set to
* 10000 the search coordinator will find all 10000 results
* prior to returning, so the initial response bundle will have the
* total set to 10000. If this value is null (or less than 10000)
* the response bundle will likely return slightly faster, but will
* not include the total. Subsequent page requests will likely
* include the total however, if they are performed after the
* search coordinator has found all results.
* For a search that returns 10000 results, if this value is set to
* 10000 the search coordinator will find all 10000 results
* prior to returning, so the initial response bundle will have the
* total set to 10000. If this value is null (or less than 10000)
* the response bundle will likely return slightly faster, but will
* not include the total. Subsequent page requests will likely
* include the total however, if they are performed after the
* search coordinator has found all results.
* </p>
* <p>
* Set this value to <code>0</code> to always load all
* results before returning.
* Set this value to <code>0</code> to always load all
* results before returning.
* </p>
*/
public Integer getCountSearchResultsUpTo() {
Expand All @@ -185,18 +186,18 @@ public Integer getCountSearchResultsUpTo() {
* whether a "total count" is included in the response bundle for searches that
* return large amounts of data.
* <p>
* For a search that returns 10000 results, if this value is set to
* 10000 the search coordinator will find all 10000 results
* prior to returning, so the initial response bundle will have the
* total set to 10000. If this value is null (or less than 10000)
* the response bundle will likely return slightly faster, but will
* not include the total. Subsequent page requests will likely
* include the total however, if they are performed after the
* search coordinator has found all results.
* For a search that returns 10000 results, if this value is set to
* 10000 the search coordinator will find all 10000 results
* prior to returning, so the initial response bundle will have the
* total set to 10000. If this value is null (or less than 10000)
* the response bundle will likely return slightly faster, but will
* not include the total. Subsequent page requests will likely
* include the total however, if they are performed after the
* search coordinator has found all results.
* </p>
* <p>
* Set this value to <code>0</code> to always load all
* results before returning.
* Set this value to <code>0</code> to always load all
* results before returning.
* </p>
*/
public void setCountSearchResultsUpTo(Integer theCountSearchResultsUpTo) {
Expand Down Expand Up @@ -408,8 +409,11 @@ public List<IServerInterceptor> getInterceptors() {
/**
* This may be used to optionally register server interceptors directly against the DAOs.
*/
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
myInterceptors = theInterceptors;
public void setInterceptors(IServerInterceptor... theInterceptor) {
setInterceptors(new ArrayList<IServerInterceptor>());
if (theInterceptor != null && theInterceptor.length != 0) {
getInterceptors().addAll(Arrays.asList(theInterceptor));
}
}

/**
Expand Down Expand Up @@ -466,6 +470,25 @@ public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
myResourceEncoding = theResourceEncoding;
}

/**
* This setting configures the strategy to use in generating IDs for newly
* created resources on the server. The default is {@link IdStrategyEnum#SEQUENTIAL_NUMERIC}.
*/
public IdStrategyEnum getResourceIdStrategy() {
return myResourceIdStrategy;
}

/**
* This setting configures the strategy to use in generating IDs for newly
* created resources on the server. The default is {@link IdStrategyEnum#SEQUENTIAL_NUMERIC}.
*
* @param theResourceIdStrategy The strategy. Must not be null.
*/
public void setResourceIdStrategy(IdStrategyEnum theResourceIdStrategy) {
Validate.notNull(theResourceIdStrategy, "theResourceIdStrategy must not be null");
myResourceIdStrategy = theResourceIdStrategy;
}

/**
* If set, an individual resource will not be allowed to have more than the
* given number of tags, profiles, and security labels (the limit is for the combined
Expand Down Expand Up @@ -1007,11 +1030,8 @@ public void setHardSearchLimit(int theHardSearchLimit) {
/**
* This may be used to optionally register server interceptors directly against the DAOs.
*/
public void setInterceptors(IServerInterceptor... theInterceptor) {
setInterceptors(new ArrayList<IServerInterceptor>());
if (theInterceptor != null && theInterceptor.length != 0) {
getInterceptors().addAll(Arrays.asList(theInterceptor));
}
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
myInterceptors = theInterceptors;
}

/**
Expand Down Expand Up @@ -1062,4 +1082,16 @@ public enum IndexEnabledEnum {
DISABLED
}

public enum IdStrategyEnum {
/**
* This strategy is the default strategy, and it simply uses a sequential
* numeric ID for each newly created resource.
*/
SEQUENTIAL_NUMERIC,
/**
* Each resource will receive a randomly generated UUID
*/
UUID
}

}
@@ -0,0 +1,86 @@
package ca.uhn.fhir.jpa.dao.r4;

import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.Assert.*;

public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CreateTest.class);

@After
public void afterResetDao() {
myDaoConfig.setResourceIdStrategy(new DaoConfig().getResourceIdStrategy());
}

@Test
public void testCreateWithUuidResourceStrategy() throws Exception {
myDaoConfig.setResourceIdStrategy(DaoConfig.IdStrategyEnum.UUID);

Patient p = new Patient();
p.addName().setFamily("FAM");
IIdType id = myPatientDao.create(p).getId().toUnqualified();

assertThat(id.getIdPart(), matchesPattern("[a-z0-9]{8}-.*"));

p = myPatientDao.read(id);
assertEquals("FAM", p.getNameFirstRep().getFamily());

}

@Test
public void testTransactionCreateWithUuidResourceStrategy() throws Exception {
myDaoConfig.setResourceIdStrategy(DaoConfig.IdStrategyEnum.UUID);

Organization org = new Organization();
org.setId(IdType.newRandomUuid());
org.setName("ORG");

Patient p = new Patient();
p.setId(IdType.newRandomUuid());
p.addName().setFamily("FAM");
p.getManagingOrganization().setReference(org.getId());

Bundle input = new Bundle();
input.setType(Bundle.BundleType.TRANSACTION);
input.addEntry()
.setResource(org)
.setFullUrl(org.getId())
.getRequest()
.setMethod(Bundle.HTTPVerb.POST);
input.addEntry()
.setResource(p)
.setFullUrl(p.getId())
.getRequest()
.setMethod(Bundle.HTTPVerb.POST);

ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(input));

Bundle output = mySystemDao.transaction(mySrd, input);

ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));

assertThat(output.getEntry().get(0).getResponse().getLocation(), matchesPattern("Organization/[a-z0-9]{8}-.*"));
assertThat(output.getEntry().get(1).getResponse().getLocation(), matchesPattern("Patient/[a-z0-9]{8}-.*"));


}


@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}

}
4 changes: 4 additions & 0 deletions src/changes/changes.xml
Expand Up @@ -21,6 +21,10 @@
Searching in JPA server using a combination of _content and _id parameters
failed. Thanks to Jeff Weyer for reporting!
</action>
<action type="add">
A new configuration option has been added to DaoConfig which allows newly created
resources to be assigned a UUID by the server instead of a sequential ID
</action>
</release>
<release version="3.1.0" date="2017-11-23">
<action type="add">
Expand Down

0 comments on commit 36e719f

Please sign in to comment.