Fix #1355 - Failed to call access method: java.lang.NullPointerException in case of bundle entry without resource
This commit is contained in:
parent
2543f27697
commit
989e26fdb4
|
@ -96,6 +96,8 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulUpdate=Successfully update
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}". Value search parameters for this search are: {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.TransactionProcessor.missingMandatoryResource=Missing required resource in Bundle.entry[{1}].resource for operation {0}
|
||||
|
||||
ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor.externalReferenceNotAllowed=Resource contains external reference to URL "{0}" but this server is not configured to allow external references
|
||||
ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor.failedToExtractPaths=Failed to extract values from resource using FHIRPath "{0}": {1}
|
||||
|
||||
|
|
|
@ -246,16 +246,11 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
|
||||
BaseServerResponseExceptionHolder caughtEx = new BaseServerResponseExceptionHolder();
|
||||
|
||||
TransactionCallback<BUNDLE> callback = theStatus -> {
|
||||
try {
|
||||
BUNDLE subRequestBundle = myVersionAdapter.createBundle(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION.toCode());
|
||||
myVersionAdapter.addEntry(subRequestBundle, nextRequestEntry);
|
||||
|
||||
return processTransactionAsSubRequest((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
|
||||
};
|
||||
|
||||
try {
|
||||
// FIXME: this doesn't need to be a callback
|
||||
BUNDLE nextResponseBundle = callback.doInTransaction(null);
|
||||
BUNDLE nextResponseBundle = processTransactionAsSubRequest((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
|
||||
|
||||
BUNDLEENTRY subResponseEntry = myVersionAdapter.getEntries(nextResponseBundle).get(0);
|
||||
myVersionAdapter.addEntry(resp, subResponseEntry);
|
||||
|
@ -362,7 +357,7 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
placeholderIds.add(fullUrl);
|
||||
}
|
||||
}
|
||||
Collections.sort(entries, new TransactionSorter(placeholderIds));
|
||||
entries.sort(new TransactionSorter(placeholderIds));
|
||||
|
||||
/*
|
||||
* All of the write operations in the transaction (PUT, POST, etc.. basically anything
|
||||
|
@ -638,6 +633,7 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
switch (verb) {
|
||||
case "POST": {
|
||||
// CREATE
|
||||
validateResourcePresent(res, order, verb);
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||
res.setId((String) null);
|
||||
|
@ -695,6 +691,7 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
}
|
||||
case "PUT": {
|
||||
// UPDATE
|
||||
validateResourcePresent(res, order, verb);
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
|
||||
|
||||
|
@ -908,6 +905,13 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
|
|||
}
|
||||
}
|
||||
|
||||
private void validateResourcePresent(IBaseResource theResource, Integer theOrder, String theVerb) {
|
||||
if (theResource == null) {
|
||||
String msg = myContext.getLocalizer().getMessage(TransactionProcessor.class, "missingMandatoryResource", theVerb, theOrder);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private IIdType newIdType(String theResourceType, String theResourceId, String theVersion) {
|
||||
org.hl7.fhir.r4.model.IdType id = new org.hl7.fhir.r4.model.IdType(theResourceType, theResourceId, theVersion);
|
||||
return myContext.getVersion().newIdType().setValue(id.getValue());
|
||||
|
|
|
@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.dstu3.model.codesystems.IssueType;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -65,7 +66,10 @@ public class TransactionProcessorVersionAdapterDstu3 implements TransactionProce
|
|||
@Override
|
||||
public void populateEntryWithOperationOutcome(BaseServerResponseException theCaughtEx, Bundle.BundleEntryComponent theEntry) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(OperationOutcome.IssueSeverity.ERROR).setDiagnostics(theCaughtEx.getMessage());
|
||||
oo.addIssue()
|
||||
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
|
||||
.setDiagnostics(theCaughtEx.getMessage())
|
||||
.setCode(OperationOutcome.IssueType.EXCEPTION);
|
||||
theEntry.getResponse().setOutcome(oo);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,10 @@ public class TransactionProcessorVersionAdapterR4 implements TransactionProcesso
|
|||
@Override
|
||||
public void populateEntryWithOperationOutcome(BaseServerResponseException theCaughtEx, Bundle.BundleEntryComponent theEntry) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(OperationOutcome.IssueSeverity.ERROR).setDiagnostics(theCaughtEx.getMessage());
|
||||
oo.addIssue()
|
||||
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
|
||||
.setDiagnostics(theCaughtEx.getMessage())
|
||||
.setCode(OperationOutcome.IssueType.EXCEPTION);
|
||||
theEntry.getResponse().setOutcome(oo);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.jpa.dao.*;
|
||||
import ca.uhn.fhir.jpa.dao.data.*;
|
||||
|
@ -9,7 +10,6 @@ import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor;
|
|||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -17,7 +17,6 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
|||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||
|
@ -32,9 +31,12 @@ import ca.uhn.fhir.rest.server.BasePagingProvider;
|
|||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||
import org.hibernate.search.jpa.Search;
|
||||
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
|
@ -42,6 +44,7 @@ import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
|
|||
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
|
||||
import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent;
|
||||
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||
import org.hl7.fhir.r4.utils.IResourceValidator;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
|
@ -290,12 +293,12 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
protected ICacheWarmingSvc myCacheWarmingSvc;
|
||||
@Autowired
|
||||
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
|
||||
@Autowired
|
||||
protected SubscriptionRegistry mySubscriptionRegistry;
|
||||
|
||||
@Autowired
|
||||
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
|
||||
private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor;
|
||||
private List<Object> mySystemInterceptors;
|
||||
private FhirValidator myValidator;
|
||||
|
||||
@After()
|
||||
public void afterCleanupDao() {
|
||||
|
@ -388,6 +391,20 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
|||
return newJsonParser.parseResource(type, string);
|
||||
}
|
||||
|
||||
protected void validate(IBaseResource theResource) {
|
||||
if (myValidator == null) {
|
||||
myValidator = myFhirCtx.newValidator();
|
||||
FhirInstanceValidator validator = new FhirInstanceValidator();
|
||||
validator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore);
|
||||
myValidator.registerValidatorModule(validator);
|
||||
}
|
||||
ValidationResult result = myValidator.validateWithResult(theResource);
|
||||
if (!result.isSuccessful()) {
|
||||
fail(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContextBaseJpaR4Test() throws Exception {
|
||||
ourValueSetDao.purgeCaches();
|
||||
|
|
|
@ -14,9 +14,13 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Bundle.*;
|
||||
|
@ -796,6 +800,83 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionMissingResourceForPost() {
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleType.TRANSACTION);
|
||||
request
|
||||
.addEntry()
|
||||
.setFullUrl("Patient/")
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.POST)
|
||||
.setUrl("Patient/");
|
||||
|
||||
try {
|
||||
mySystemDao.transaction(mySrd, request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Missing required resource in Bundle.entry[0].resource for operation POST", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionMissingResourceForPut() {
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleType.TRANSACTION);
|
||||
request
|
||||
.addEntry()
|
||||
.setFullUrl("Patient/123")
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.PUT)
|
||||
.setUrl("Patient/123");
|
||||
|
||||
try {
|
||||
mySystemDao.transaction(mySrd, request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Missing required resource in Bundle.entry[0].resource for operation PUT", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchMissingResourceForPost() {
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleType.BATCH);
|
||||
request
|
||||
.addEntry()
|
||||
.setFullUrl("Patient/")
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.POST)
|
||||
.setUrl("Patient/");
|
||||
|
||||
Bundle outcome = mySystemDao.transaction(mySrd, request);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
assertEquals("400 Bad Request", outcome.getEntry().get(0).getResponse().getStatus());
|
||||
assertEquals(IssueSeverity.ERROR, ((OperationOutcome)outcome.getEntry().get(0).getResponse().getOutcome()).getIssueFirstRep().getSeverity());
|
||||
assertEquals("Missing required resource in Bundle.entry[0].resource for operation POST", ((OperationOutcome)outcome.getEntry().get(0).getResponse().getOutcome()).getIssueFirstRep().getDiagnostics());
|
||||
validate(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchMissingResourceForPut() {
|
||||
Bundle request = new Bundle();
|
||||
request.setType(BundleType.BATCH);
|
||||
request
|
||||
.addEntry()
|
||||
.setFullUrl("Patient/123")
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.PUT)
|
||||
.setUrl("Patient/123");
|
||||
|
||||
Bundle outcome = mySystemDao.transaction(mySrd, request);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
assertEquals("400 Bad Request", outcome.getEntry().get(0).getResponse().getStatus());
|
||||
assertEquals(IssueSeverity.ERROR, ((OperationOutcome)outcome.getEntry().get(0).getResponse().getOutcome()).getIssueFirstRep().getSeverity());
|
||||
assertEquals("Missing required resource in Bundle.entry[0].resource for operation PUT", ((OperationOutcome)outcome.getEntry().get(0).getResponse().getOutcome()).getIssueFirstRep().getDiagnostics());
|
||||
validate(outcome);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateInlineMatchUrlWithOneMatch() {
|
||||
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";
|
||||
|
|
|
@ -100,6 +100,12 @@
|
|||
Uploading the LOINC/RSNA Radiology Playbook would occasionally fail when evaluating part type names
|
||||
due to case sensitivity. This has been corrected.
|
||||
</action>
|
||||
<action type="fix" issue="1355">
|
||||
Invoking the transaction or batch operation on the JPA server would fail
|
||||
with a NullPointerException if the Bundle passed in did not contain
|
||||
a resource in an entry that required a resource (e.g. a POST). Thanks to
|
||||
GitHub user @lytvynenko-dmitriy for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.8.0" date="2019-05-30" description="Hippo">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue