JPA server is now able to handle placeholder IDs (e.g. urn:uuid:00....000) being used in Bundle.entry.request.url as a part of the conditional URL within transactions.
This commit is contained in:
parent
2e60ff7521
commit
b13333c3c0
|
@ -63,6 +63,7 @@ import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||||
|
|
||||||
public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu3.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu3.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -136,115 +137,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractTransactionUrlOrThrowException(BundleEntryComponent nextEntry, HTTPVerb verb) {
|
|
||||||
String url = nextEntry.getRequest().getUrl();
|
|
||||||
if (isBlank(url)) {
|
|
||||||
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionMissingUrl", verb.name()));
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called for nested bundles (e.g. if we received a transaction with an entry that
|
|
||||||
* was a GET search, this method is called on the bundle for the search result, that will be placed in the
|
|
||||||
* outer bundle). This method applies the _summary and _content parameters to the output of
|
|
||||||
* that bundle.
|
|
||||||
*
|
|
||||||
* TODO: This isn't the most efficient way of doing this.. hopefully we can come up with something better in the future.
|
|
||||||
*/
|
|
||||||
private IBaseResource filterNestedBundle(RequestDetails theRequestDetails, IBaseResource theResource) {
|
|
||||||
IParser p = getContext().newJsonParser();
|
|
||||||
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
|
|
||||||
return p.parseResource(theResource.getClass(), p.encodeResourceToString(theResource));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
|
|
||||||
IFhirResourceDao<? extends IBaseResource> retVal = getDao(theClass);
|
|
||||||
if (retVal == null) {
|
|
||||||
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + getContext().getResourceDefinition(theClass).getName());
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Meta metaGetOperation(RequestDetails theRequestDetails) {
|
|
||||||
// Notify interceptors
|
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails);
|
|
||||||
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
|
|
||||||
|
|
||||||
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
|
|
||||||
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
|
||||||
List<TagDefinition> tagDefinitions = q.getResultList();
|
|
||||||
|
|
||||||
Meta retVal = toMeta(tagDefinitions);
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Meta toMeta(Collection<TagDefinition> tagDefinitions) {
|
|
||||||
Meta retVal = new Meta();
|
|
||||||
for (TagDefinition next : tagDefinitions) {
|
|
||||||
switch (next.getTagType()) {
|
|
||||||
case PROFILE:
|
|
||||||
retVal.addProfile(next.getCode());
|
|
||||||
break;
|
|
||||||
case SECURITY_LABEL:
|
|
||||||
retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
|
|
||||||
break;
|
|
||||||
case TAG:
|
|
||||||
retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
|
|
||||||
RuntimeResourceDefinition resType;
|
|
||||||
try {
|
|
||||||
resType = getContext().getResourceDefinition(theParts.getResourceType());
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
IFhirResourceDao<? extends IBaseResource> dao = null;
|
|
||||||
if (resType != null) {
|
|
||||||
dao = getDao(resType.getImplementingClass());
|
|
||||||
}
|
|
||||||
if (dao == null) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (theParts.getResourceId() == null && theParts.getParams() == null) {
|
|
||||||
// String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
|
||||||
// throw new InvalidRequestException(msg);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return dao;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
|
||||||
@Override
|
|
||||||
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
|
|
||||||
if (theRequestDetails != null) {
|
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null);
|
|
||||||
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
String actionName = "Transaction";
|
|
||||||
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
|
||||||
super.markRequestAsProcessingSubRequest(theRequestDetails);
|
|
||||||
try {
|
|
||||||
return doTransaction(theRequestDetails, theRequest, theActionName);
|
|
||||||
} finally {
|
|
||||||
super.clearRequestAsProcessingSubRequest(theRequestDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
||||||
BundleType transactionType = theRequest.getTypeElement().getValue();
|
BundleType transactionType = theRequest.getTypeElement().getValue();
|
||||||
|
@ -299,7 +191,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
getEntries.add(theRequest.getEntry().get(i));
|
getEntries.add(theRequest.getEntry().get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(theRequest.getEntry(), new TransactionSorter());
|
|
||||||
|
|
||||||
Set<String> deletedResources = new HashSet<String>();
|
Set<String> deletedResources = new HashSet<String>();
|
||||||
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
|
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
|
||||||
|
@ -307,17 +198,32 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
|
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
|
||||||
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
|
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>();
|
||||||
|
|
||||||
|
List<BundleEntryComponent> entries = new ArrayList<BundleEntryComponent>(theRequest.getEntry());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See FhirSystemDaoDstu3Test#testTransactionWithPlaceholderIdInMatchUrl
|
||||||
|
* Basically if the resource has a match URL that references a placeholder,
|
||||||
|
* we try to handle the resource with the placeholder first.
|
||||||
|
*/
|
||||||
|
Set<String> placeholderIds = new HashSet<String>();
|
||||||
|
for (BundleEntryComponent nextEntry : entries) {
|
||||||
|
if (isNotBlank(nextEntry.getFullUrl()) && nextEntry.getFullUrl().startsWith(IdType.URN_PREFIX)) {
|
||||||
|
placeholderIds.add(nextEntry.getFullUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(entries, new TransactionSorter(placeholderIds));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through the request and process any entries of type
|
* Loop through the request and process any entries of type
|
||||||
* PUT, POST or DELETE
|
* PUT, POST or DELETE
|
||||||
*/
|
*/
|
||||||
for (int i = 0; i < theRequest.getEntry().size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
|
||||||
if (i % 100 == 0) {
|
if (i % 100 == 0) {
|
||||||
ourLog.info("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size());
|
ourLog.info("Processed {} non-GET entries out of {}", i, entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
BundleEntryComponent nextReqEntry = theRequest.getEntry().get(i);
|
BundleEntryComponent nextReqEntry = entries.get(i);
|
||||||
Resource res = nextReqEntry.getResource();
|
Resource res = nextReqEntry.getResource();
|
||||||
IdType nextResourceId = null;
|
IdType nextResourceId = null;
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
|
@ -368,6 +274,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
res.setId((String) null);
|
res.setId((String) null);
|
||||||
DaoMethodOutcome outcome;
|
DaoMethodOutcome outcome;
|
||||||
String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
|
String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
|
||||||
|
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl);
|
||||||
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
|
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
|
||||||
if (nextResourceId != null) {
|
if (nextResourceId != null) {
|
||||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
|
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
|
||||||
|
@ -399,7 +306,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams(), deleteConflicts, theRequestDetails);
|
String matchUrl = parts.getResourceType() + '?' + parts.getParams();
|
||||||
|
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl);
|
||||||
|
DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails);
|
||||||
List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities();
|
List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities();
|
||||||
for (ResourceTable deleted : allDeleted) {
|
for (ResourceTable deleted : allDeleted) {
|
||||||
deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless().getValueAsString());
|
deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless().getValueAsString());
|
||||||
|
@ -430,6 +339,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
} else {
|
} else {
|
||||||
res.setId((String) null);
|
res.setId((String) null);
|
||||||
String matchUrl = parts.getResourceType() + '?' + parts.getParams();
|
String matchUrl = parts.getResourceType() + '?' + parts.getParams();
|
||||||
|
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl);
|
||||||
outcome = resourceDao.update(res, matchUrl, false, theRequestDetails);
|
outcome = resourceDao.update(res, matchUrl, false, theRequestDetails);
|
||||||
if (Boolean.TRUE.equals(outcome.getCreated())) {
|
if (Boolean.TRUE.equals(outcome.getCreated())) {
|
||||||
conditionalRequestUrls.put(matchUrl, res.getClass());
|
conditionalRequestUrls.put(matchUrl, res.getClass());
|
||||||
|
@ -607,6 +517,131 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractTransactionUrlOrThrowException(BundleEntryComponent nextEntry, HTTPVerb verb) {
|
||||||
|
String url = nextEntry.getRequest().getUrl();
|
||||||
|
if (isBlank(url)) {
|
||||||
|
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionMissingUrl", verb.name()));
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called for nested bundles (e.g. if we received a transaction with an entry that
|
||||||
|
* was a GET search, this method is called on the bundle for the search result, that will be placed in the
|
||||||
|
* outer bundle). This method applies the _summary and _content parameters to the output of
|
||||||
|
* that bundle.
|
||||||
|
*
|
||||||
|
* TODO: This isn't the most efficient way of doing this.. hopefully we can come up with something better in the future.
|
||||||
|
*/
|
||||||
|
private IBaseResource filterNestedBundle(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||||
|
IParser p = getContext().newJsonParser();
|
||||||
|
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
|
||||||
|
return p.parseResource(theResource.getClass(), p.encodeResourceToString(theResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
|
||||||
|
IFhirResourceDao<? extends IBaseResource> retVal = getDao(theClass);
|
||||||
|
if (retVal == null) {
|
||||||
|
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + getContext().getResourceDefinition(theClass).getName());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Meta metaGetOperation(RequestDetails theRequestDetails) {
|
||||||
|
// Notify interceptors
|
||||||
|
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails);
|
||||||
|
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
|
||||||
|
|
||||||
|
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
|
||||||
|
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
||||||
|
List<TagDefinition> tagDefinitions = q.getResultList();
|
||||||
|
|
||||||
|
Meta retVal = toMeta(tagDefinitions);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
|
||||||
|
String matchUrl = theMatchUrl;
|
||||||
|
if (isNotBlank(matchUrl)) {
|
||||||
|
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
|
||||||
|
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
|
||||||
|
IdType nextReplacementId = nextSubstitutionEntry.getValue();
|
||||||
|
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
|
||||||
|
String nextReplacementIdPart = nextReplacementId.getValueAsString();
|
||||||
|
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
|
||||||
|
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
|
||||||
|
RuntimeResourceDefinition resType;
|
||||||
|
try {
|
||||||
|
resType = getContext().getResourceDefinition(theParts.getResourceType());
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
IFhirResourceDao<? extends IBaseResource> dao = null;
|
||||||
|
if (resType != null) {
|
||||||
|
dao = getDao(resType.getImplementingClass());
|
||||||
|
}
|
||||||
|
if (dao == null) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (theParts.getResourceId() == null && theParts.getParams() == null) {
|
||||||
|
// String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
|
||||||
|
// throw new InvalidRequestException(msg);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Meta toMeta(Collection<TagDefinition> tagDefinitions) {
|
||||||
|
Meta retVal = new Meta();
|
||||||
|
for (TagDefinition next : tagDefinitions) {
|
||||||
|
switch (next.getTagType()) {
|
||||||
|
case PROFILE:
|
||||||
|
retVal.addProfile(next.getCode());
|
||||||
|
break;
|
||||||
|
case SECURITY_LABEL:
|
||||||
|
retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
|
||||||
|
break;
|
||||||
|
case TAG:
|
||||||
|
retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
@Override
|
||||||
|
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
|
||||||
|
if (theRequestDetails != null) {
|
||||||
|
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null);
|
||||||
|
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
String actionName = "Transaction";
|
||||||
|
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
||||||
|
super.markRequestAsProcessingSubRequest(theRequestDetails);
|
||||||
|
try {
|
||||||
|
return doTransaction(theRequestDetails, theRequest, theActionName);
|
||||||
|
} finally {
|
||||||
|
super.clearRequestAsProcessingSubRequest(theRequestDetails);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, IdType nextResourceId, DaoMethodOutcome outcome,
|
private static void handleTransactionCreateOrUpdateOutcome(Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, IdType nextResourceId, DaoMethodOutcome outcome,
|
||||||
BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes, ServletRequestDetails theRequestDetails) {
|
BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes, ServletRequestDetails theRequestDetails) {
|
||||||
IdType newId = (IdType) outcome.getId().toUnqualifiedVersionless();
|
IdType newId = (IdType) outcome.getId().toUnqualifiedVersionless();
|
||||||
|
@ -655,7 +690,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
|
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
/**
|
/**
|
||||||
* Transaction Order, per the spec:
|
* Transaction Order, per the spec:
|
||||||
*
|
*
|
||||||
|
@ -664,37 +698,94 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
* Process any PUT interactions
|
* Process any PUT interactions
|
||||||
* Process any GET interactions
|
* Process any GET interactions
|
||||||
*/
|
*/
|
||||||
//@formatter:off
|
|
||||||
public class TransactionSorter implements Comparator<BundleEntryComponent> {
|
public class TransactionSorter implements Comparator<BundleEntryComponent> {
|
||||||
|
|
||||||
|
private Set<String> myPlaceholderIds;
|
||||||
|
|
||||||
|
public TransactionSorter(Set<String> thePlaceholderIds) {
|
||||||
|
myPlaceholderIds = thePlaceholderIds;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(BundleEntryComponent theO1, BundleEntryComponent theO2) {
|
public int compare(BundleEntryComponent theO1, BundleEntryComponent theO2) {
|
||||||
int o1 = toOrder(theO1);
|
int o1 = toOrder(theO1);
|
||||||
int o2 = toOrder(theO2);
|
int o2 = toOrder(theO2);
|
||||||
|
|
||||||
|
if (o1 == o2) {
|
||||||
|
String matchUrl1 = toMatchUrl(theO1);
|
||||||
|
String matchUrl2 = toMatchUrl(theO2);
|
||||||
|
if (isBlank(matchUrl1) && isBlank(matchUrl2)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (isBlank(matchUrl1)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (isBlank(matchUrl2)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean match1containsSubstitutions = false;
|
||||||
|
boolean match2containsSubstitutions = false;
|
||||||
|
for (String nextPlaceholder : myPlaceholderIds) {
|
||||||
|
if (matchUrl1.contains(nextPlaceholder)) {
|
||||||
|
match1containsSubstitutions = true;
|
||||||
|
}
|
||||||
|
if (matchUrl2.contains(nextPlaceholder)) {
|
||||||
|
match2containsSubstitutions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match1containsSubstitutions && match2containsSubstitutions) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!match1containsSubstitutions && !match2containsSubstitutions) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (match1containsSubstitutions) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return o1 - o2;
|
return o1 - o2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toMatchUrl(BundleEntryComponent theEntry) {
|
||||||
|
HTTPVerb verb = theEntry.getRequest().getMethod();
|
||||||
|
if (verb == HTTPVerb.POST) {
|
||||||
|
return theEntry.getRequest().getIfNoneExist();
|
||||||
|
}
|
||||||
|
if (verb == HTTPVerb.PUT || verb == HTTPVerb.DELETE) {
|
||||||
|
String url = extractTransactionUrlOrThrowException(theEntry, verb);
|
||||||
|
UrlParts parts = UrlUtil.parseUrl(url);
|
||||||
|
if (isBlank(parts.getResourceId())) {
|
||||||
|
return parts.getResourceType() + '?' + parts.getParams();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private int toOrder(BundleEntryComponent theO1) {
|
private int toOrder(BundleEntryComponent theO1) {
|
||||||
int o1 = 0;
|
int o1 = 0;
|
||||||
if (theO1.getRequest().getMethodElement().getValue() != null) {
|
if (theO1.getRequest().getMethodElement().getValue() != null) {
|
||||||
switch (theO1.getRequest().getMethodElement().getValue()) {
|
switch (theO1.getRequest().getMethodElement().getValue()) {
|
||||||
case DELETE:
|
case DELETE:
|
||||||
o1 = 1;
|
o1 = 1;
|
||||||
break;
|
break;
|
||||||
case POST:
|
case POST:
|
||||||
o1 = 2;
|
o1 = 2;
|
||||||
break;
|
break;
|
||||||
case PUT:
|
case PUT:
|
||||||
o1 = 3;
|
o1 = 3;
|
||||||
break;
|
break;
|
||||||
case GET:
|
case GET:
|
||||||
o1 = 4;
|
o1 = 4;
|
||||||
break;
|
break;
|
||||||
case NULL:
|
case NULL:
|
||||||
o1 = 0;
|
o1 = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return o1;
|
return o1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,21 @@ import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.*;
|
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryResponseComponent;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||||
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
|
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
@ -51,6 +60,100 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Bundle createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb theVerb) {
|
||||||
|
|
||||||
|
Patient pat = new Patient();
|
||||||
|
pat
|
||||||
|
.addIdentifier()
|
||||||
|
.setSystem("http://acme.org")
|
||||||
|
.setValue("ID1");
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs
|
||||||
|
.getCode()
|
||||||
|
.addCoding()
|
||||||
|
.setSystem("http://loinc.org")
|
||||||
|
.setCode("29463-7");
|
||||||
|
obs.setEffective(new DateTimeType("2011-09-03T11:13:00-04:00"));
|
||||||
|
obs.setValue(new Quantity()
|
||||||
|
.setValue(new BigDecimal("123.4"))
|
||||||
|
.setCode("kg")
|
||||||
|
.setSystem("http://unitsofmeasure.org")
|
||||||
|
.setUnit("kg"));
|
||||||
|
obs.getSubject().setReference("urn:uuid:0001");
|
||||||
|
|
||||||
|
Observation obs2 = new Observation();
|
||||||
|
obs2
|
||||||
|
.getCode()
|
||||||
|
.addCoding()
|
||||||
|
.setSystem("http://loinc.org")
|
||||||
|
.setCode("29463-7");
|
||||||
|
obs2.setEffective(new DateTimeType("2017-09-03T11:13:00-04:00"));
|
||||||
|
obs2.setValue(new Quantity()
|
||||||
|
.setValue(new BigDecimal("123.4"))
|
||||||
|
.setCode("kg")
|
||||||
|
.setSystem("http://unitsofmeasure.org")
|
||||||
|
.setUnit("kg"));
|
||||||
|
obs2.getSubject().setReference("urn:uuid:0001");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put one observation before the patient it references, and
|
||||||
|
* one after it just to make sure that order doesn't matter
|
||||||
|
*/
|
||||||
|
Bundle input = new Bundle();
|
||||||
|
input.setType(BundleType.TRANSACTION);
|
||||||
|
|
||||||
|
if (theVerb == HTTPVerb.PUT) {
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0002")
|
||||||
|
.setResource(obs)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Observation?subject=urn:uuid:0001&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00");
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0001")
|
||||||
|
.setResource(pat)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Patient?identifier=http%3A%2F%2Facme.org|ID1");
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0003")
|
||||||
|
.setResource(obs2)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.PUT)
|
||||||
|
.setUrl("Observation?subject=urn:uuid:0001&code=http%3A%2F%2Floinc.org|29463-7&date=2017-09-03T11:13:00-04:00");
|
||||||
|
} else if (theVerb == HTTPVerb.POST) {
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0002")
|
||||||
|
.setResource(obs)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Observation")
|
||||||
|
.setIfNoneExist("Observation?subject=urn:uuid:0001&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00");
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0001")
|
||||||
|
.setResource(pat)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Patient")
|
||||||
|
.setIfNoneExist("Patient?identifier=http%3A%2F%2Facme.org|ID1");
|
||||||
|
input
|
||||||
|
.addEntry()
|
||||||
|
.setFullUrl("urn:uuid:0003")
|
||||||
|
.setResource(obs2)
|
||||||
|
.getRequest()
|
||||||
|
.setMethod(HTTPVerb.POST)
|
||||||
|
.setUrl("Observation")
|
||||||
|
.setIfNoneExist("Observation?subject=urn:uuid:0001&code=http%3A%2F%2Floinc.org|29463-7&date=2017-09-03T11:13:00-04:00");
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T extends org.hl7.fhir.dstu3.model.Resource> T find(Bundle theBundle, Class<T> theType, int theIndex) {
|
private <T extends org.hl7.fhir.dstu3.model.Resource> T find(Bundle theBundle, Class<T> theType, int theIndex) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -200,36 +303,36 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
rpt = myDiagnosticReportDao.read(rptId);
|
rpt = myDiagnosticReportDao.read(rptId);
|
||||||
assertThat(rpt.getResult(), empty());
|
assertThat(rpt.getResult(), empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForTransaction() {
|
public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForTransaction() {
|
||||||
Bundle bundle;
|
Bundle bundle;
|
||||||
|
|
||||||
// First time
|
// First time
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
p.setActive(true);
|
p.setActive(true);
|
||||||
bundle = new Bundle();
|
bundle = new Bundle();
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
bundle
|
bundle
|
||||||
.addEntry()
|
.addEntry()
|
||||||
.setResource(p)
|
.setResource(p)
|
||||||
.setFullUrl("Patient/A")
|
.setFullUrl("Patient/A")
|
||||||
.getRequest()
|
.getRequest()
|
||||||
.setMethod(HTTPVerb.PUT)
|
.setMethod(HTTPVerb.PUT)
|
||||||
.setUrl("Patient/A");
|
.setUrl("Patient/A");
|
||||||
Bundle resp = mySystemDao.transaction(mySrd, bundle);
|
Bundle resp = mySystemDao.transaction(mySrd, bundle);
|
||||||
assertThat(resp.getEntry().get(0).getResponse().getLocation(), endsWith("Patient/A/_history/1"));
|
assertThat(resp.getEntry().get(0).getResponse().getLocation(), endsWith("Patient/A/_history/1"));
|
||||||
|
|
||||||
// Second time should not result in an update
|
// Second time should not result in an update
|
||||||
p = new Patient();
|
p = new Patient();
|
||||||
p.setActive(true);
|
p.setActive(true);
|
||||||
bundle = new Bundle();
|
bundle = new Bundle();
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
bundle
|
bundle
|
||||||
.addEntry()
|
.addEntry()
|
||||||
.setResource(p)
|
.setResource(p)
|
||||||
.setFullUrl("Patient/A")
|
.setFullUrl("Patient/A")
|
||||||
.getRequest()
|
.getRequest()
|
||||||
.setMethod(HTTPVerb.PUT)
|
.setMethod(HTTPVerb.PUT)
|
||||||
.setUrl("Patient/A");
|
.setUrl("Patient/A");
|
||||||
resp = mySystemDao.transaction(mySrd, bundle);
|
resp = mySystemDao.transaction(mySrd, bundle);
|
||||||
|
@ -241,10 +344,10 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
bundle = new Bundle();
|
bundle = new Bundle();
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
bundle
|
bundle
|
||||||
.addEntry()
|
.addEntry()
|
||||||
.setResource(p)
|
.setResource(p)
|
||||||
.setFullUrl("Patient/A")
|
.setFullUrl("Patient/A")
|
||||||
.getRequest()
|
.getRequest()
|
||||||
.setMethod(HTTPVerb.PUT)
|
.setMethod(HTTPVerb.PUT)
|
||||||
.setUrl("Patient/A");
|
.setUrl("Patient/A");
|
||||||
resp = mySystemDao.transaction(mySrd, bundle);
|
resp = mySystemDao.transaction(mySrd, bundle);
|
||||||
|
@ -256,13 +359,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
myPatientDao.read(new IdType("Patient/A/_history/2"));
|
myPatientDao.read(new IdType("Patient/A/_history/2"));
|
||||||
fail();
|
fail();
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
//good
|
// good
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
myPatientDao.read(new IdType("Patient/A/_history/3"));
|
myPatientDao.read(new IdType("Patient/A/_history/3"));
|
||||||
fail();
|
fail();
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
//good
|
// good
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,60 +590,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTransactionWithReferenceUuid() {
|
|
||||||
Bundle request = new Bundle();
|
|
||||||
|
|
||||||
Patient p = new Patient();
|
|
||||||
p.setActive(true);
|
|
||||||
p.setId(IdType.newRandomUuid());
|
|
||||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId());
|
|
||||||
|
|
||||||
Observation o = new Observation();
|
|
||||||
o.getCode().setText("Some Observation");
|
|
||||||
o.getSubject().setReference(p.getId());
|
|
||||||
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST);
|
|
||||||
|
|
||||||
Bundle resp = mySystemDao.transaction(mySrd, request);
|
|
||||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
|
||||||
|
|
||||||
String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue();
|
|
||||||
assertThat(patientId, startsWith("Patient/"));
|
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.setLoadSynchronous(true);
|
|
||||||
params.add("subject", new ReferenceParam(patientId));
|
|
||||||
IBundleProvider found = myObservationDao.search(params);
|
|
||||||
assertEquals(1, found.size().intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTransactionWithReferenceResource() {
|
|
||||||
Bundle request = new Bundle();
|
|
||||||
|
|
||||||
Patient p = new Patient();
|
|
||||||
p.setActive(true);
|
|
||||||
p.setId(IdType.newRandomUuid());
|
|
||||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId());
|
|
||||||
|
|
||||||
Observation o = new Observation();
|
|
||||||
o.getCode().setText("Some Observation");
|
|
||||||
o.getSubject().setResource(p);
|
|
||||||
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST);
|
|
||||||
|
|
||||||
Bundle resp = mySystemDao.transaction(mySrd, request);
|
|
||||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
|
||||||
|
|
||||||
String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue();
|
|
||||||
assertThat(patientId, startsWith("Patient/"));
|
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.setLoadSynchronous(true);
|
|
||||||
params.add("subject", new ReferenceParam(patientId));
|
|
||||||
IBundleProvider found = myObservationDao.search(params);
|
|
||||||
assertEquals(1, found.size().intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionCreateInlineMatchUrlWithOneMatch() {
|
public void testTransactionCreateInlineMatchUrlWithOneMatch() {
|
||||||
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";
|
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";
|
||||||
|
@ -1335,7 +1384,8 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
mySystemDao.transaction(mySrd, inputBundle);
|
mySystemDao.transaction(mySrd, inputBundle);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("Unable to process Transaction - Request would cause multiple resources to match URL: \"Encounter?identifier=urn:foo|12345\". Does transaction request contain duplicates?", e.getMessage());
|
assertEquals("Unable to process Transaction - Request would cause multiple resources to match URL: \"Encounter?identifier=urn:foo|12345\". Does transaction request contain duplicates?",
|
||||||
|
e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1365,9 +1415,10 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
mySystemDao.transaction(mySrd, inputBundle);
|
mySystemDao.transaction(mySrd, inputBundle);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("Unable to process Transaction - Request would cause multiple resources to match URL: \"Encounter?identifier=urn:foo|12345\". Does transaction request contain duplicates?", e.getMessage());
|
assertEquals("Unable to process Transaction - Request would cause multiple resources to match URL: \"Encounter?identifier=urn:foo|12345\". Does transaction request contain duplicates?",
|
||||||
|
e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidRequestException.class)
|
@Test(expected = InvalidRequestException.class)
|
||||||
|
@ -2026,8 +2077,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||||
|
|
||||||
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
||||||
|
|
||||||
|
|
||||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||||
@Override
|
@Override
|
||||||
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
|
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
|
||||||
|
@ -2040,7 +2090,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2101,6 +2151,66 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
assertEquals(id0.toUnqualifiedVersionless().getValue(), app2.getParticipant().get(1).getActor().getReference());
|
assertEquals(id0.toUnqualifiedVersionless().getValue(), app2.getParticipant().get(1).getActor().getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure we are able to handle placeholder IDs in match URLs, e.g.
|
||||||
|
*
|
||||||
|
* "request": {
|
||||||
|
* "method": "PUT",
|
||||||
|
* "url": "Observation?subject=urn:uuid:8dba64a8-2aca-48fe-8b4e-8c7bf2ab695a&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00"
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithPlaceholderIdInMatchUrlPut() {
|
||||||
|
|
||||||
|
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
|
||||||
|
Bundle output = mySystemDao.transaction(null, input);
|
||||||
|
|
||||||
|
assertEquals("201 Created", output.getEntry().get(0).getResponse().getStatus());
|
||||||
|
assertEquals("201 Created", output.getEntry().get(1).getResponse().getStatus());
|
||||||
|
assertEquals("201 Created", output.getEntry().get(2).getResponse().getStatus());
|
||||||
|
|
||||||
|
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.PUT);
|
||||||
|
Bundle output2 = mySystemDao.transaction(null, input2);
|
||||||
|
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output2));
|
||||||
|
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(0).getResponse().getStatus());
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(1).getResponse().getStatus());
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(2).getResponse().getStatus());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure we are able to handle placeholder IDs in match URLs, e.g.
|
||||||
|
*
|
||||||
|
* "request": {
|
||||||
|
* "method": "PUT",
|
||||||
|
* "url": "Observation?subject=urn:uuid:8dba64a8-2aca-48fe-8b4e-8c7bf2ab695a&code=http%3A%2F%2Floinc.org|29463-7&date=2011-09-03T11:13:00-04:00"
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithPlaceholderIdInMatchUrlPost() {
|
||||||
|
|
||||||
|
Bundle input = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
|
||||||
|
Bundle output = mySystemDao.transaction(null, input);
|
||||||
|
|
||||||
|
assertEquals("201 Created", output.getEntry().get(0).getResponse().getStatus());
|
||||||
|
assertEquals("201 Created", output.getEntry().get(1).getResponse().getStatus());
|
||||||
|
assertEquals("201 Created", output.getEntry().get(2).getResponse().getStatus());
|
||||||
|
|
||||||
|
Bundle input2 = createInputTransactionWithPlaceholderIdInMatchUrl(HTTPVerb.POST);
|
||||||
|
Bundle output2 = mySystemDao.transaction(null, input2);
|
||||||
|
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output2));
|
||||||
|
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(0).getResponse().getStatus());
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(1).getResponse().getStatus());
|
||||||
|
assertEquals("200 OK", output2.getEntry().get(2).getResponse().getStatus());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per a message on the mailing list
|
* Per a message on the mailing list
|
||||||
*/
|
*/
|
||||||
|
@ -2137,6 +2247,86 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
|
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithReferenceResource() {
|
||||||
|
Bundle request = new Bundle();
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
p.setId(IdType.newRandomUuid());
|
||||||
|
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId());
|
||||||
|
|
||||||
|
Observation o = new Observation();
|
||||||
|
o.getCode().setText("Some Observation");
|
||||||
|
o.getSubject().setResource(p);
|
||||||
|
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
|
||||||
|
Bundle resp = mySystemDao.transaction(mySrd, request);
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||||
|
|
||||||
|
String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue();
|
||||||
|
assertThat(patientId, startsWith("Patient/"));
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.setLoadSynchronous(true);
|
||||||
|
params.add("subject", new ReferenceParam(patientId));
|
||||||
|
IBundleProvider found = myObservationDao.search(params);
|
||||||
|
assertEquals(1, found.size().intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithReferenceToCreateIfNoneExist() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
|
|
||||||
|
Medication med = new Medication();
|
||||||
|
IdType medId = IdType.newRandomUuid();
|
||||||
|
med.setId(medId);
|
||||||
|
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
|
||||||
|
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
|
||||||
|
|
||||||
|
MedicationRequest mo = new MedicationRequest();
|
||||||
|
mo.setMedication(new Reference(medId));
|
||||||
|
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
|
||||||
|
ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
|
|
||||||
|
Bundle outcome = mySystemDao.transaction(mySrd, bundle);
|
||||||
|
ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||||
|
|
||||||
|
IdType medId1 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
|
||||||
|
IdType medOrderId1 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Again!
|
||||||
|
*/
|
||||||
|
|
||||||
|
bundle = new Bundle();
|
||||||
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
|
|
||||||
|
med = new Medication();
|
||||||
|
medId = IdType.newRandomUuid();
|
||||||
|
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
|
||||||
|
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
|
||||||
|
|
||||||
|
mo = new MedicationRequest();
|
||||||
|
mo.setMedication(new Reference(medId));
|
||||||
|
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
|
||||||
|
outcome = mySystemDao.transaction(mySrd, bundle);
|
||||||
|
|
||||||
|
IdType medId2 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
|
||||||
|
IdType medOrderId2 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
||||||
|
|
||||||
|
assertTrue(medId1.isIdPartValidLong());
|
||||||
|
assertTrue(medId2.isIdPartValidLong());
|
||||||
|
assertTrue(medOrderId1.isIdPartValidLong());
|
||||||
|
assertTrue(medOrderId2.isIdPartValidLong());
|
||||||
|
|
||||||
|
assertEquals(medId1, medId2);
|
||||||
|
assertNotEquals(medOrderId1, medOrderId2);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// /**
|
// /**
|
||||||
|
@ -2240,59 +2430,32 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionWithReferenceToCreateIfNoneExist() {
|
public void testTransactionWithReferenceUuid() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle request = new Bundle();
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
|
||||||
|
|
||||||
Medication med = new Medication();
|
Patient p = new Patient();
|
||||||
IdType medId = IdType.newRandomUuid();
|
p.setActive(true);
|
||||||
med.setId(medId);
|
p.setId(IdType.newRandomUuid());
|
||||||
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
|
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl(p.getId());
|
||||||
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
|
|
||||||
|
|
||||||
MedicationRequest mo = new MedicationRequest();
|
Observation o = new Observation();
|
||||||
mo.setMedication(new Reference(medId));
|
o.getCode().setText("Some Observation");
|
||||||
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
|
o.getSubject().setReference(p.getId());
|
||||||
|
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST);
|
||||||
|
|
||||||
ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
Bundle resp = mySystemDao.transaction(mySrd, request);
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||||
|
|
||||||
Bundle outcome = mySystemDao.transaction(mySrd, bundle);
|
String patientId = new IdType(resp.getEntry().get(0).getResponse().getLocation()).toUnqualifiedVersionless().getValue();
|
||||||
ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
assertThat(patientId, startsWith("Patient/"));
|
||||||
|
|
||||||
IdType medId1 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
IdType medOrderId1 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
params.setLoadSynchronous(true);
|
||||||
|
params.add("subject", new ReferenceParam(patientId));
|
||||||
/*
|
IBundleProvider found = myObservationDao.search(params);
|
||||||
* Again!
|
assertEquals(1, found.size().intValue());
|
||||||
*/
|
|
||||||
|
|
||||||
bundle = new Bundle();
|
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
|
||||||
|
|
||||||
med = new Medication();
|
|
||||||
medId = IdType.newRandomUuid();
|
|
||||||
med.getCode().addCoding().setSystem("billscodes").setCode("theCode");
|
|
||||||
bundle.addEntry().setResource(med).setFullUrl(medId.getValue()).getRequest().setMethod(HTTPVerb.POST).setIfNoneExist("Medication?code=billscodes|theCode");
|
|
||||||
|
|
||||||
mo = new MedicationRequest();
|
|
||||||
mo.setMedication(new Reference(medId));
|
|
||||||
bundle.addEntry().setResource(mo).setFullUrl(mo.getIdElement().getValue()).getRequest().setMethod(HTTPVerb.POST);
|
|
||||||
|
|
||||||
outcome = mySystemDao.transaction(mySrd, bundle);
|
|
||||||
|
|
||||||
IdType medId2 = new IdType(outcome.getEntry().get(0).getResponse().getLocation());
|
|
||||||
IdType medOrderId2 = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
|
||||||
|
|
||||||
assertTrue(medId1.isIdPartValidLong());
|
|
||||||
assertTrue(medId2.isIdPartValidLong());
|
|
||||||
assertTrue(medOrderId1.isIdPartValidLong());
|
|
||||||
assertTrue(medOrderId2.isIdPartValidLong());
|
|
||||||
|
|
||||||
assertEquals(medId1, medId2);
|
|
||||||
assertNotEquals(medOrderId1, medOrderId2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionWithRelativeOidIds() throws Exception {
|
public void testTransactionWithRelativeOidIds() throws Exception {
|
||||||
Bundle res = new Bundle();
|
Bundle res = new Bundle();
|
||||||
|
@ -2330,7 +2493,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
||||||
assertThat(o2.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart()));
|
assertThat(o2.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is not the correct way to do it, but we'll allow it to be lenient
|
* This is not the correct way to do it, but we'll allow it to be lenient
|
||||||
|
|
|
@ -86,6 +86,8 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
*/
|
*/
|
||||||
@DatatypeDef(name = "id", profileOf=StringType.class)
|
@DatatypeDef(name = "id", profileOf=StringType.class)
|
||||||
public final class IdType extends UriType implements IPrimitiveType<String>, IIdType {
|
public final class IdType extends UriType implements IPrimitiveType<String>, IIdType {
|
||||||
|
public static final String URN_PREFIX = "urn:";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the maximum length for the ID
|
* This is the maximum length for the ID
|
||||||
*/
|
*/
|
||||||
|
@ -486,8 +488,8 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
|
||||||
return defaultString(myUnqualifiedId).startsWith("#");
|
return defaultString(myUnqualifiedId).startsWith("#");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUrn() {
|
public boolean isUrn() {
|
||||||
return defaultString(myUnqualifiedId).startsWith("urn:");
|
return defaultString(myUnqualifiedId).startsWith(URN_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -526,7 +528,7 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
|
||||||
myUnqualifiedVersionId = null;
|
myUnqualifiedVersionId = null;
|
||||||
myResourceType = null;
|
myResourceType = null;
|
||||||
myHaveComponentParts = true;
|
myHaveComponentParts = true;
|
||||||
} else if (theValue.startsWith("urn:")) {
|
} else if (theValue.startsWith(URN_PREFIX)) {
|
||||||
myBaseUrl = null;
|
myBaseUrl = null;
|
||||||
myUnqualifiedId = theValue;
|
myUnqualifiedId = theValue;
|
||||||
myUnqualifiedVersionId = null;
|
myUnqualifiedVersionId = null;
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
meant that any project using ookhttp would import both structures
|
meant that any project using ookhttp would import both structures
|
||||||
JARs. This has been removed.
|
JARs. This has been removed.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
JPA server is now able to handle placeholder IDs (e.g. urn:uuid:00....000)
|
||||||
|
being used in Bundle.entry.request.url as a part of the conditional URL
|
||||||
|
within transactions.
|
||||||
|
</action>
|
||||||
</release
|
</release
|
||||||
<release version="2.6" date="TBD">
|
<release version="2.6" date="TBD">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue