Add trasnaction BATCH support to JPA
This commit is contained in:
parent
d9d192cc04
commit
5b09a3d2b6
|
@ -33,10 +33,9 @@ public class Constants {
|
|||
public static final String CHARSETNAME_UTF_8 = "UTF-8";
|
||||
public static final String CT_ATOM_XML = "application/atom+xml";
|
||||
public static final String CT_FHIR_JSON = "application/json+fhir";
|
||||
|
||||
public static final String CTSUFFIX_CHARSET_UTF8 = "; charset=" + CHARSETNAME_UTF_8;
|
||||
public static final String CT_FHIR_XML = "application/xml+fhir";
|
||||
public static final String CT_HTML = "text/html";
|
||||
public static final String CTSUFFIX_CHARSET_UTF8 = "; charset=" + CHARSETNAME_UTF_8;
|
||||
public static final String CT_HTML_WITH_UTF8 = "text/html" + CTSUFFIX_CHARSET_UTF8;
|
||||
public static final String CT_JSON = "application/json";
|
||||
public static final String CT_OCTET_STREAM = "application/octet-stream";
|
||||
|
@ -82,8 +81,13 @@ public class Constants {
|
|||
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
|
||||
public static final String HEADER_LOCATION = "Location";
|
||||
public static final String HEADER_LOCATION_LC = HEADER_LOCATION.toLowerCase();
|
||||
public static final String HEADER_PREFER = "Prefer";
|
||||
public static final String HEADER_PREFER_RETURN = "return";
|
||||
public static final String HEADER_PREFER_RETURN_MINIMAL = "minimal";
|
||||
public static final String HEADER_PREFER_RETURN_REPRESENTATION = "representation";
|
||||
public static final String HEADER_SUFFIX_CT_UTF_8 = "; charset=UTF-8";
|
||||
public static final String HEADERVALUE_CORS_ALLOW_METHODS_ALL = "GET, POST, PUT, DELETE, OPTIONS";
|
||||
public static final Map<Integer, String> HTTP_STATUS_NAMES;
|
||||
public static final String LINK_FHIR_BASE = "fhir-base";
|
||||
public static final String LINK_FIRST = "first";
|
||||
public static final String LINK_LAST = "last";
|
||||
|
@ -109,8 +113,8 @@ public class Constants {
|
|||
public static final String PARAM_SORT = "_sort";
|
||||
public static final String PARAM_SORT_ASC = "_sort:asc";
|
||||
public static final String PARAM_SORT_DESC = "_sort:desc";
|
||||
public static final String PARAM_TAGS = "_tags";
|
||||
public static final String PARAM_TAG = "_tag";
|
||||
public static final String PARAM_TAGS = "_tags";
|
||||
public static final String PARAM_VALIDATE = "_validate";
|
||||
public static final String PARAMQUALIFIER_MISSING = ":missing";
|
||||
public static final String PARAMQUALIFIER_MISSING_FALSE = "false";
|
||||
|
@ -123,7 +127,7 @@ public class Constants {
|
|||
public static final int STATUS_HTTP_304_NOT_MODIFIED = 304;
|
||||
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
|
||||
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
|
||||
public static final int STATUS_HTTP_403_FORBIDDEN= 403;
|
||||
public static final int STATUS_HTTP_403_FORBIDDEN = 403;
|
||||
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
|
||||
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
|
||||
public static final int STATUS_HTTP_409_CONFLICT = 409;
|
||||
|
@ -134,10 +138,6 @@ public class Constants {
|
|||
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
|
||||
public static final String URL_TOKEN_HISTORY = "_history";
|
||||
public static final String URL_TOKEN_METADATA = "metadata";
|
||||
public static final String HEADER_PREFER = "Prefer";
|
||||
public static final String HEADER_PREFER_RETURN = "return";
|
||||
public static final String HEADER_PREFER_RETURN_MINIMAL = "minimal";
|
||||
public static final String HEADER_PREFER_RETURN_REPRESENTATION = "representation";
|
||||
|
||||
static {
|
||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||
|
@ -161,8 +161,74 @@ public class Constants {
|
|||
}
|
||||
|
||||
FORMAT_VAL_TO_ENCODING = Collections.unmodifiableMap(valToEncoding);
|
||||
|
||||
|
||||
CHARSET_UTF8 = Charset.forName(CHARSETNAME_UTF_8);
|
||||
|
||||
HashMap<Integer, String> statusNames = new HashMap<Integer, String>();
|
||||
|
||||
statusNames.put(200, "OK");
|
||||
statusNames.put(201, "Created");
|
||||
statusNames.put(202, "Accepted");
|
||||
statusNames.put(203, "Non-Authoritative Information");
|
||||
statusNames.put(204, "No Content");
|
||||
statusNames.put(205, "Reset Content");
|
||||
statusNames.put(206, "Partial Content");
|
||||
statusNames.put(207, "Multi-Status");
|
||||
statusNames.put(208, "Already Reported");
|
||||
statusNames.put(226, "IM Used");
|
||||
statusNames.put(300, "Multiple Choices");
|
||||
statusNames.put(301, "Moved Permanently");
|
||||
statusNames.put(302, "Found");
|
||||
statusNames.put(302, "Moved Temporarily");
|
||||
statusNames.put(303, "See Other");
|
||||
statusNames.put(304, "Not Modified");
|
||||
statusNames.put(305, "Use Proxy");
|
||||
statusNames.put(307, "Temporary Redirect");
|
||||
statusNames.put(308, "Permanent Redirect");
|
||||
statusNames.put(400, "Bad Request");
|
||||
statusNames.put(401, "Unauthorized");
|
||||
statusNames.put(402, "Payment Required");
|
||||
statusNames.put(403, "Forbidden");
|
||||
statusNames.put(404, "Not Found");
|
||||
statusNames.put(405, "Method Not Allowed");
|
||||
statusNames.put(406, "Not Acceptable");
|
||||
statusNames.put(407, "Proxy Authentication Required");
|
||||
statusNames.put(408, "Request Timeout");
|
||||
statusNames.put(409, "Conflict");
|
||||
statusNames.put(410, "Gone");
|
||||
statusNames.put(411, "Length Required");
|
||||
statusNames.put(412, "Precondition Failed");
|
||||
statusNames.put(413, "Payload Too Large");
|
||||
statusNames.put(413, "Request Entity Too Large");
|
||||
statusNames.put(414, "URI Too Long");
|
||||
statusNames.put(414, "Request-URI Too Long");
|
||||
statusNames.put(415, "Unsupported Media Type");
|
||||
statusNames.put(416, "Requested range not satisfiable");
|
||||
statusNames.put(417, "Expectation Failed");
|
||||
statusNames.put(418, "I'm a teapot");
|
||||
statusNames.put(419, "Insufficient Space On Resource");
|
||||
statusNames.put(420, "Method Failure");
|
||||
statusNames.put(421, "Destination Locked");
|
||||
statusNames.put(422, "Unprocessable Entity");
|
||||
statusNames.put(423, "Locked");
|
||||
statusNames.put(424, "Failed Dependency");
|
||||
statusNames.put(426, "Upgrade Required");
|
||||
statusNames.put(428, "Precondition Required");
|
||||
statusNames.put(429, "Too Many Requests");
|
||||
statusNames.put(431, "Request Header Fields Too Large");
|
||||
statusNames.put(500, "Internal Server Error");
|
||||
statusNames.put(501, "Not Implemented");
|
||||
statusNames.put(502, "Bad Gateway");
|
||||
statusNames.put(503, "Service Unavailable");
|
||||
statusNames.put(504, "Gateway Timeout");
|
||||
statusNames.put(505, "HTTP Version not supported");
|
||||
statusNames.put(506, "Variant Also Negotiates");
|
||||
statusNames.put(507, "Insufficient Storage");
|
||||
statusNames.put(508, "Loop Detected");
|
||||
statusNames.put(509, "Bandwidth Limit Exceeded");
|
||||
statusNames.put(510, "Not Extended");
|
||||
statusNames.put(511, "Network Authentication Required");
|
||||
HTTP_STATUS_NAMES = Collections.unmodifiableMap(statusNames);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -358,6 +358,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
return pids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> processMatchUrl(String theMatchUrl) {
|
||||
return processMatchUrl(theMatchUrl, getResourceType());
|
||||
}
|
||||
|
||||
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates,
|
||||
IQueryParameterType nextOr) {
|
||||
boolean missingFalse = false;
|
||||
|
|
|
@ -65,6 +65,5 @@ public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResou
|
|||
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
|
||||
return oo;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class FhirResourceDaoQuestionnaireAnswersDstu2 extends FhirResourceDaoDst
|
|||
super.validateResourceForStorage(theResource);
|
||||
|
||||
QuestionnaireAnswers qa = (QuestionnaireAnswers) theResource;
|
||||
if (qa.getQuestionnaire().getReference().isEmpty()) {
|
||||
if (qa == null || qa.getQuestionnaire() == null || qa.getQuestionnaire().getReference() == null || qa.getQuestionnaire().getReference().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -31,8 +33,13 @@ import java.util.Set;
|
|||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
|
@ -47,18 +54,24 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class);
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTxManager;
|
||||
|
||||
private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) {
|
||||
String url = nextEntry.getRequest().getUrl();
|
||||
if (isBlank(url)) {
|
||||
|
@ -67,6 +80,79 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
return url;
|
||||
}
|
||||
|
||||
private Bundle batch(Bundle theRequest) {
|
||||
ourLog.info("Beginning batch with {} resources", theRequest.getEntry().size());
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
|
||||
|
||||
Bundle resp = new Bundle();
|
||||
resp.setType(BundleTypeEnum.BATCH_RESPONSE);
|
||||
OperationOutcome ooResp = new OperationOutcome();
|
||||
resp.addEntry().setResource(ooResp);
|
||||
|
||||
/*
|
||||
* For batch, we handle each entry as a mini-transaction in its own
|
||||
* database transaction so that if one fails, it doesn't prevent others
|
||||
*/
|
||||
|
||||
for (final Entry nextRequestEntry : theRequest.getEntry()) {
|
||||
|
||||
TransactionCallback<Bundle> callback = new TransactionCallback<Bundle>() {
|
||||
@Override
|
||||
public Bundle doInTransaction(TransactionStatus theStatus) {
|
||||
Bundle subRequestBundle = new Bundle();
|
||||
subRequestBundle.setType(BundleTypeEnum.TRANSACTION);
|
||||
subRequestBundle.addEntry(nextRequestEntry);
|
||||
|
||||
Bundle subResponseBundle = transaction(subRequestBundle, "Batch sub-request");
|
||||
return subResponseBundle;
|
||||
}
|
||||
};
|
||||
|
||||
BaseServerResponseException caughtEx;
|
||||
try {
|
||||
Bundle nextResponseBundle = txTemplate.execute(callback);
|
||||
caughtEx = null;
|
||||
|
||||
Entry subResponseEntry = nextResponseBundle.getEntry().get(1);
|
||||
resp.addEntry(subResponseEntry);
|
||||
/*
|
||||
* If the individual entry didn't have a resource in its response, bring the
|
||||
* sub-transaction's OperationOutcome across so the client can see it
|
||||
*/
|
||||
if (subResponseEntry.getResource() == null) {
|
||||
subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
|
||||
}
|
||||
|
||||
} catch (BaseServerResponseException e) {
|
||||
caughtEx = e;
|
||||
} catch (Throwable t) {
|
||||
ourLog.error("Failure during BATCH sub transaction processing", t);
|
||||
caughtEx = new InternalErrorException(t);
|
||||
}
|
||||
|
||||
if (caughtEx != null) {
|
||||
Entry nextEntry = resp.addEntry();
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDetails(caughtEx.getMessage());
|
||||
nextEntry.setResource(oo);
|
||||
|
||||
EntryResponse nextEntryResp = nextEntry.getResponse();
|
||||
nextEntryResp.setStatus(toStatusString(caughtEx.getStatusCode()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Batch completed in {}ms", new Object[] { delay });
|
||||
ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Batch completed in " + delay + "ms");
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaDt metaGetOperation() {
|
||||
|
||||
|
@ -148,11 +234,33 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public Bundle transaction(Bundle theResources) {
|
||||
ourLog.info("Beginning transaction with {} resources", theResources.getEntry().size());
|
||||
public Bundle transaction(Bundle theRequest) {
|
||||
String theActionName = "Transaction";
|
||||
return transaction(theRequest, theActionName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Bundle transaction(Bundle theRequest, String theActionName) {
|
||||
BundleTypeEnum transactionType = theRequest.getTypeElement().getValueAsEnum();
|
||||
if (transactionType == BundleTypeEnum.BATCH) {
|
||||
return batch(theRequest);
|
||||
}
|
||||
|
||||
OperationOutcome statusOperationOutcome = new OperationOutcome();
|
||||
if (transactionType == null) {
|
||||
String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleTypeEnum.TRANSACTION.getCode();
|
||||
statusOperationOutcome.addIssue().setCode(IssueTypeEnum.INVALID_CONTENT).setSeverity(IssueSeverityEnum.WARNING).setDetails(message);
|
||||
ourLog.warn(message);
|
||||
transactionType = BundleTypeEnum.TRANSACTION;
|
||||
}
|
||||
if (transactionType != BundleTypeEnum.TRANSACTION) {
|
||||
throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + transactionType.getCode());
|
||||
}
|
||||
|
||||
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Set<IdDt> allIds = new LinkedHashSet<IdDt>();
|
||||
|
@ -160,11 +268,12 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
Map<IdDt, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdDt, DaoMethodOutcome>();
|
||||
|
||||
Bundle response = new Bundle();
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
response.addEntry().setResource(oo);
|
||||
response.addEntry().setResource(statusOperationOutcome);
|
||||
|
||||
for (int i = 0; i < theResources.getEntry().size(); i++) {
|
||||
Entry nextEntry = theResources.getEntry().get(i);
|
||||
// TODO: process verbs in the correct order
|
||||
|
||||
for (int i = 0; i < theRequest.getEntry().size(); i++) {
|
||||
Entry nextEntry = theRequest.getEntry().get(i);
|
||||
IResource res = nextEntry.getResource();
|
||||
IdDt nextResourceId = null;
|
||||
if (res != null) {
|
||||
|
@ -221,7 +330,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
|
||||
}
|
||||
|
||||
newEntry.getResponse().setStatus(Integer.toString(Constants.STATUS_HTTP_204_NO_CONTENT));
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_204_NO_CONTENT));
|
||||
break;
|
||||
}
|
||||
case PUT: {
|
||||
|
@ -258,7 +367,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
if (isNotBlank(ifNoneMatch)) {
|
||||
ifNoneMatch = MethodUtil.parseETagValue(ifNoneMatch);
|
||||
}
|
||||
|
||||
|
||||
if (parts.getResourceId() != null && parts.getParams() == null) {
|
||||
IResource found;
|
||||
boolean notChanged = false;
|
||||
|
@ -281,9 +390,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
resp.setLocation(found.getId().toUnqualified().getValue());
|
||||
resp.setEtag(found.getId().getVersionIdPart());
|
||||
if (!notChanged) {
|
||||
resp.setStatus(Integer.toString(Constants.STATUS_HTTP_200_OK));
|
||||
resp.setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
} else {
|
||||
resp.setStatus(Integer.toString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
||||
resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
|
||||
}
|
||||
} else if (parts.getParams() != null) {
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType());
|
||||
|
@ -295,7 +404,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
|
||||
int configuredMax = 100; // this should probably be configurable or something
|
||||
if (bundle.size() > configuredMax) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING)
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.WARNING)
|
||||
.setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
|
||||
}
|
||||
List<IBaseResource> resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
|
||||
|
@ -305,7 +414,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
|
||||
Entry newEntry = response.addEntry();
|
||||
newEntry.setResource(searchBundle);
|
||||
newEntry.getResponse().setStatus(Integer.toString(Constants.STATUS_HTTP_200_OK));
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,6 +423,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
|
||||
FhirTerser terser = getContext().newTerser();
|
||||
|
||||
/*
|
||||
* Perform ID substitutions and then index each resource we have saved
|
||||
*/
|
||||
|
||||
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) {
|
||||
IResource nextResource = (IResource) nextOutcome.getResource();
|
||||
if (nextResource == null) {
|
||||
|
@ -337,10 +450,29 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false);
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Transaction completed in {}ms", new Object[] { delay });
|
||||
myEntityManager.flush();
|
||||
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Transaction completed in " + delay + "ms");
|
||||
/*
|
||||
* Double check we didn't allow any duplicates we shouldn't have
|
||||
*/
|
||||
for (Entry nextEntry : theRequest.getEntry()) {
|
||||
if (nextEntry.getRequest().getMethodElement().getValueAsEnum() == HTTPVerbEnum.POST) {
|
||||
String matchUrl = nextEntry.getRequest().getIfNoneExist();
|
||||
if (isNotBlank(matchUrl)) {
|
||||
IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass());
|
||||
Set<Long> val = resourceDao.processMatchUrl(matchUrl);
|
||||
if (val.size() > 1) {
|
||||
throw new InvalidRequestException(
|
||||
"Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
|
||||
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails(theActionName + " completed in " + delay + "ms");
|
||||
|
||||
for (IdDt next : allIds) {
|
||||
IdDt replacement = idSubstitutions.get(next);
|
||||
|
@ -350,7 +482,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
if (replacement.equals(next)) {
|
||||
continue;
|
||||
}
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
|
||||
}
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
@ -374,9 +506,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
}
|
||||
idToPersistedOutcome.put(newId, outcome);
|
||||
if (outcome.getCreated().booleanValue()) {
|
||||
newEntry.getResponse().setStatus(Long.toString(Constants.STATUS_HTTP_201_CREATED));
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_201_CREATED));
|
||||
} else {
|
||||
newEntry.getResponse().setStatus(Long.toString(Constants.STATUS_HTTP_200_OK));
|
||||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
}
|
||||
newEntry.getResponse().setLocation(outcome.getId().toUnqualified().getValue());
|
||||
newEntry.getResponse().setEtag(outcome.getId().getVersionIdPart());
|
||||
|
@ -389,6 +521,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static String toStatusString(int theStatusCode) {
|
||||
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
|
||||
}
|
||||
|
||||
private static class UrlParts {
|
||||
private IFhirResourceDao<? extends IBaseResource> myDao;
|
||||
private String myParams;
|
||||
|
|
|
@ -90,6 +90,8 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
*/
|
||||
MetaDt metaGetOperation(IIdType theId);
|
||||
|
||||
Set<Long> processMatchUrl(String theMatchUrl);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param theId
|
||||
|
|
|
@ -29,13 +29,16 @@ import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
|||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryResponse;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -172,12 +175,12 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
Entry respEntry = resp.getEntry().get(1);
|
||||
assertEquals(Constants.STATUS_HTTP_200_OK + "", respEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_200_OK + " OK", respEntry.getResponse().getStatus());
|
||||
assertThat(respEntry.getResponse().getLocation(), endsWith("Patient/" + id.getIdPart() + "/_history/1"));
|
||||
assertEquals("1", respEntry.getResponse().getEtag());
|
||||
|
||||
respEntry = resp.getEntry().get(2);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus());
|
||||
assertThat(respEntry.getResponse().getLocation(), containsString("Observation/"));
|
||||
assertThat(respEntry.getResponse().getLocation(), endsWith("/_history/1"));
|
||||
assertEquals("1", respEntry.getResponse().getEtag());
|
||||
|
@ -188,6 +191,116 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithInvalidType() {
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleTypeEnum.SEARCH_RESULTS);
|
||||
Patient p = new Patient();
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
|
||||
|
||||
try {
|
||||
ourSystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unable to process transaction where incoming Bundle.type = searchset", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionBatchWithFailingRead() {
|
||||
String methodName = "testTransactionBatchWithFailingRead";
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleTypeEnum.BATCH);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily(methodName);
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
|
||||
|
||||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient/THIS_ID_DOESNT_EXIST");
|
||||
|
||||
Bundle resp = ourSystemDao.transaction(request);
|
||||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
EntryResponse respEntry;
|
||||
|
||||
// Bundle.entry[0] is operation outcome
|
||||
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
|
||||
assertEquals(IssueSeverityEnum.INFORMATION, ((OperationOutcome)resp.getEntry().get(0).getResource()).getIssue().get(0).getSeverityElement().getValueAsEnum());
|
||||
assertThat(((OperationOutcome)resp.getEntry().get(0).getResource()).getIssue().get(0).getDetails(), startsWith("Batch completed in "));
|
||||
|
||||
// Bundle.entry[1] is create response
|
||||
assertEquals(OperationOutcome.class, resp.getEntry().get(1).getResource().getClass());
|
||||
assertEquals(IssueSeverityEnum.INFORMATION, ((OperationOutcome)resp.getEntry().get(1).getResource()).getIssue().get(0).getSeverityElement().getValueAsEnum());
|
||||
assertThat(((OperationOutcome)resp.getEntry().get(1).getResource()).getIssue().get(0).getDetails(), startsWith("Batch sub-request completed in"));
|
||||
assertEquals("201 Created", resp.getEntry().get(1).getResponse().getStatus());
|
||||
assertThat(resp.getEntry().get(1).getResponse().getLocation(), startsWith("Patient/"));
|
||||
|
||||
// Bundle.entry[2] is failed read response
|
||||
assertEquals(OperationOutcome.class, resp.getEntry().get(2).getResource().getClass());
|
||||
assertEquals(IssueSeverityEnum.ERROR, ((OperationOutcome)resp.getEntry().get(2).getResource()).getIssue().get(0).getSeverityElement().getValueAsEnum());
|
||||
assertEquals("Resource Patient/THIS_ID_DOESNT_EXIST is not known", ((OperationOutcome)resp.getEntry().get(2).getResource()).getIssue().get(0).getDetails());
|
||||
assertEquals("404 Not Found", resp.getEntry().get(2).getResponse().getStatus());
|
||||
|
||||
// Check POST
|
||||
respEntry = resp.getEntry().get(1).getResponse();
|
||||
assertThat(respEntry.getStatus(), startsWith("201"));
|
||||
IdDt createdId = new IdDt(respEntry.getLocation());
|
||||
assertEquals("Patient", createdId.getResourceType());
|
||||
ourPatientDao.read(createdId); // shouldn't fail
|
||||
|
||||
// Check GET
|
||||
respEntry = resp.getEntry().get(2).getResponse();
|
||||
assertThat(respEntry.getStatus(), startsWith("404"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateWithDuplicateMatchUrl01() {
|
||||
String methodName = "testTransactionCreateWithDuplicateMatchUrl01";
|
||||
Bundle request = new Bundle();
|
||||
|
||||
Patient p;
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
try {
|
||||
ourSystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals(e.getMessage(),
|
||||
"Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl01\". Does transaction request contain duplicates?");
|
||||
}
|
||||
}
|
||||
|
||||
public void testTransactionCreateWithDuplicateMatchUrl02() {
|
||||
String methodName = "testTransactionCreateWithDuplicateMatchUrl02";
|
||||
Bundle request = new Bundle();
|
||||
|
||||
Patient p;
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
|
||||
|
||||
try {
|
||||
ourSystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals(e.getMessage(),
|
||||
"Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl02\". Does transaction request contain duplicates?");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateMatchUrlWithTwoMatch() {
|
||||
String methodName = "testTransactionCreateMatchUrlWithTwoMatch";
|
||||
|
@ -239,10 +352,11 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerbEnum.POST);
|
||||
|
||||
Bundle resp = ourSystemDao.transaction(request);
|
||||
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
|
||||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
Entry respEntry = resp.getEntry().get(1);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus());
|
||||
String patientId = respEntry.getResponse().getLocation();
|
||||
assertThat(patientId, not(endsWith("Patient/" + methodName + "/_history/1")));
|
||||
assertThat(patientId, (endsWith("/_history/1")));
|
||||
|
@ -250,7 +364,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals("1", respEntry.getResponse().getEtag());
|
||||
|
||||
respEntry = resp.getEntry().get(2);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus());
|
||||
assertThat(respEntry.getResponse().getLocation(), containsString("Observation/"));
|
||||
assertThat(respEntry.getResponse().getLocation(), endsWith("/_history/1"));
|
||||
assertEquals("1", respEntry.getResponse().getEtag());
|
||||
|
@ -273,7 +387,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(2, resp.getEntry().size());
|
||||
|
||||
Entry respEntry = resp.getEntry().get(1);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", respEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus());
|
||||
String patientId = respEntry.getResponse().getLocation();
|
||||
assertThat(patientId, not(containsString("test")));
|
||||
}
|
||||
|
@ -342,8 +456,8 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
Bundle resp = ourSystemDao.transaction(request);
|
||||
|
||||
assertEquals(3, resp.getEntry().size());
|
||||
assertEquals("204", resp.getEntry().get(1).getResponse().getStatus());
|
||||
assertEquals("204", resp.getEntry().get(2).getResponse().getStatus());
|
||||
assertEquals("204 No Content", resp.getEntry().get(1).getResponse().getStatus());
|
||||
assertEquals("204 No Content", resp.getEntry().get(2).getResponse().getStatus());
|
||||
|
||||
try {
|
||||
ourPatientDao.read(id1.toVersionless());
|
||||
|
@ -361,7 +475,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionDeleteMatchUrlWithOneMatch() {
|
||||
String methodName = "testTransactionDeleteMatchUrlWithOneMatch";
|
||||
|
@ -378,7 +491,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(2, resp.getEntry().size());
|
||||
|
||||
Entry nextEntry = resp.getEntry().get(1);
|
||||
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + "", nextEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + " No Content", nextEntry.getResponse().getStatus());
|
||||
|
||||
try {
|
||||
ourPatientDao.read(id.toVersionless());
|
||||
|
@ -464,7 +577,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
Bundle res = ourSystemDao.transaction(request);
|
||||
assertEquals(2, res.getEntry().size());
|
||||
|
||||
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + "", res.getEntry().get(1).getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_204_NO_CONTENT + " No Content", res.getEntry().get(1).getResponse().getStatus());
|
||||
|
||||
try {
|
||||
ourPatientDao.read(id.toVersionless());
|
||||
|
@ -584,17 +697,17 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertThat(respBundle.getTotal().intValue(), greaterThan(0));
|
||||
|
||||
// Invalid _count
|
||||
|
||||
|
||||
request = new Bundle();
|
||||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=GKJGKJG");
|
||||
try {
|
||||
ourSystemDao.transaction(request);
|
||||
ourSystemDao.transaction(request);
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals(e.getMessage(), ("Invalid _count value: GKJGKJG"));
|
||||
}
|
||||
|
||||
// Empty _count
|
||||
|
||||
|
||||
request = new Bundle();
|
||||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=");
|
||||
respBundle = ourSystemDao.transaction(request);
|
||||
|
@ -622,7 +735,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualifiedVersionless().getValue());
|
||||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualifiedVersionless().getValue()).setIfNoneMatch("W/\"" + idv1.getVersionIdPart() + "\"");
|
||||
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl(idv1.toUnqualifiedVersionless().getValue()).setIfNoneMatch("W/\"" + idv2.getVersionIdPart() + "\"");
|
||||
|
||||
|
||||
Bundle resp = ourSystemDao.transaction(request);
|
||||
|
||||
assertEquals(4, resp.getEntry().size());
|
||||
|
@ -633,17 +746,17 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertNotNull(nextEntry.getResource());
|
||||
assertEquals(Patient.class, nextEntry.getResource().getClass());
|
||||
assertEquals(idv2.toUnqualified(), nextEntry.getResource().getId().toUnqualified());
|
||||
assertEquals("200", nextEntry.getResponse().getStatus());
|
||||
assertEquals("200 OK", nextEntry.getResponse().getStatus());
|
||||
|
||||
nextEntry = resp.getEntry().get(2);
|
||||
assertNotNull(nextEntry.getResource());
|
||||
assertEquals(Patient.class, nextEntry.getResource().getClass());
|
||||
assertEquals(idv2.toUnqualified(), nextEntry.getResource().getId().toUnqualified());
|
||||
assertEquals("200", nextEntry.getResponse().getStatus());
|
||||
assertEquals("200 OK", nextEntry.getResponse().getStatus());
|
||||
|
||||
nextEntry = resp.getEntry().get(3);
|
||||
assertNull(nextEntry.getResource());
|
||||
assertEquals("304", nextEntry.getResponse().getStatus());
|
||||
assertEquals("304 Not Modified", nextEntry.getResponse().getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -671,14 +784,14 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
Entry nextEntry = resp.getEntry().get(1);
|
||||
assertEquals("200", nextEntry.getResponse().getStatus());
|
||||
assertEquals("200 OK", nextEntry.getResponse().getStatus());
|
||||
assertThat(nextEntry.getResponse().getLocation(), not(containsString("test")));
|
||||
assertEquals(id.toVersionless(), p.getId().toVersionless());
|
||||
assertNotEquals(id, p.getId());
|
||||
assertThat(p.getId().toString(), endsWith("/_history/2"));
|
||||
|
||||
nextEntry = resp.getEntry().get(1);
|
||||
assertEquals("" + Constants.STATUS_HTTP_200_OK, nextEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_200_OK + " OK", nextEntry.getResponse().getStatus());
|
||||
assertThat(nextEntry.getResponse().getLocation(), not(emptyString()));
|
||||
|
||||
nextEntry = resp.getEntry().get(2);
|
||||
|
@ -745,7 +858,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
Entry nextEntry = resp.getEntry().get(1);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + "", nextEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", nextEntry.getResponse().getStatus());
|
||||
IdDt patientId = new IdDt(nextEntry.getResponse().getLocation());
|
||||
|
||||
assertThat(nextEntry.getResponse().getLocation(), not(containsString("test")));
|
||||
|
@ -785,7 +898,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(3, resp.getEntry().size());
|
||||
|
||||
Entry nextEntry = resp.getEntry().get(1);
|
||||
assertEquals("200", nextEntry.getResponse().getStatus());
|
||||
assertEquals("200 OK", nextEntry.getResponse().getStatus());
|
||||
|
||||
assertThat(nextEntry.getResponse().getLocation(), (containsString("test")));
|
||||
assertEquals(id.toVersionless(), new IdDt(nextEntry.getResponse().getLocation()).toVersionless());
|
||||
|
@ -793,7 +906,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
assertThat(nextEntry.getResponse().getLocation(), endsWith("/_history/2"));
|
||||
|
||||
nextEntry = resp.getEntry().get(2);
|
||||
assertEquals("" + Constants.STATUS_HTTP_201_CREATED, nextEntry.getResponse().getStatus());
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", nextEntry.getResponse().getStatus());
|
||||
|
||||
o = ourObservationDao.read(new IdDt(resp.getEntry().get(2).getResponse().getLocation()));
|
||||
assertEquals(id.toVersionless(), o.getSubject().getReference());
|
||||
|
@ -925,17 +1038,17 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
|
||||
|
||||
Bundle resp = ourSystemDao.transaction(res);
|
||||
|
||||
|
||||
ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
|
||||
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
|
||||
assertEquals(4, resp.getEntry().size());
|
||||
|
||||
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
|
||||
|
||||
|
||||
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
|
||||
assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
|
||||
|
||||
|
||||
assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdDt(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdDt(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
assertTrue(resp.getEntry().get(3).getResponse().getLocation(), new IdDt(resp.getEntry().get(3).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
|
@ -973,17 +1086,17 @@ public class FhirSystemDaoDstu2Test extends BaseJpaTest {
|
|||
res.addEntry().setResource(o2).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Observation");
|
||||
|
||||
Bundle resp = ourSystemDao.transaction(res);
|
||||
|
||||
|
||||
ourLog.info(ourFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
|
||||
assertEquals(BundleTypeEnum.TRANSACTION_RESPONSE, resp.getTypeElement().getValueAsEnum());
|
||||
assertEquals(4, resp.getEntry().size());
|
||||
|
||||
assertEquals(OperationOutcome.class, resp.getEntry().get(0).getResource().getClass());
|
||||
|
||||
|
||||
OperationOutcome outcome = (OperationOutcome) resp.getEntry().get(0).getResource();
|
||||
assertThat(outcome.getIssue().get(1).getDetails(), containsString("Placeholder resource ID \"urn:oid:0.1.2.3\" was replaced with permanent ID \"Patient/"));
|
||||
|
||||
|
||||
assertTrue(resp.getEntry().get(1).getResponse().getLocation(), new IdDt(resp.getEntry().get(1).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
assertTrue(resp.getEntry().get(2).getResponse().getLocation(), new IdDt(resp.getEntry().get(2).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
assertTrue(resp.getEntry().get(3).getResponse().getLocation(), new IdDt(resp.getEntry().get(3).getResponse().getLocation()).getIdPart().matches("^[0-9]+$"));
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
<action type="add">
|
||||
JPA server and generic client now both support the _tag search parameter
|
||||
</action>
|
||||
<action type="add">
|
||||
Add support for BATCH mode to JPA server transaction operation
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.1" date="2015-07-13">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue