Improvements to AutohrizationInterceptor create handling

This commit is contained in:
James Agnew 2019-09-30 15:07:23 -04:00
parent aeef8c1ab5
commit 066c9a7fb7
23 changed files with 250 additions and 712 deletions

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.io.Serializable;
@ -629,5 +630,5 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
}
}
}

View File

@ -46,12 +46,14 @@ public class BundleUtil {
private final RequestTypeEnum myRequestType;
private final IBaseResource myResource;
private final String myUrl;
private final String myConditionalUrl;
BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) {
BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource, String theConditionalUrl) {
super();
myRequestType = theRequestType;
myUrl = theUrl;
myResource = theResource;
myConditionalUrl = theConditionalUrl;
}
public RequestTypeEnum getRequestType() {
@ -62,6 +64,10 @@ public class BundleUtil {
return myResource;
}
public String getConditionalUrl() {
return myConditionalUrl;
}
public String getUrl() {
return myUrl;
}
@ -190,19 +196,21 @@ public class BundleUtil {
BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request");
BaseRuntimeElementCompositeDefinition<?> requestElem = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request");
BaseRuntimeChildDefinition urlChild = requestElem.getChildByName("url");
BaseRuntimeChildDefinition requestUrlChild = requestElem.getChildByName("url");
BaseRuntimeChildDefinition requestIfNoneExistChild = requestElem.getChildByName("ifNoneExist");
BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method");
for (IBase nextEntry : entries) {
IBaseResource resource = null;
String url = null;
RequestTypeEnum requestType = null;
String conditionalUrl = null;
for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
resource = (IBaseResource) next;
}
for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) {
for (IBase nextUrl : urlChild.getAccessor().getValues(nextRequest)) {
for (IBase nextUrl : requestUrlChild.getAccessor().getValues(nextRequest)) {
url = ((IPrimitiveType<?>) nextUrl).getValueAsString();
}
for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) {
@ -211,13 +219,29 @@ public class BundleUtil {
requestType = RequestTypeEnum.valueOf(methodString);
}
}
if (requestType != null) {
//noinspection EnumSwitchStatementWhichMissesCases
switch (requestType) {
case PUT:
conditionalUrl = url != null && url.contains("?") ? url : null;
break;
case POST:
List<IBase> ifNoneExistReps = requestIfNoneExistChild.getAccessor().getValues(nextRequest);
if (ifNoneExistReps.size() > 0) {
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) ifNoneExistReps.get(0);
conditionalUrl = ifNoneExist.getValueAsString();
}
break;
}
}
}
/*
* All 3 might be null - That's ok because we still want to know the
* order in the original bundle.
*/
retVal.add(new BundleEntryParts(requestType, url, resource));
retVal.add(new BundleEntryParts(requestType, url, resource, conditionalUrl));
}

View File

@ -1540,53 +1540,6 @@ public class GenericOkHttpClientDstu2Test {
assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass());
}
@Test
public void testTransactionWithListOfResources() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle();
resp.addEntry().getResponse().setLocation("Patient/1/_history/1");
resp.addEntry().getResponse().setLocation("Patient/2/_history/2");
String respString = ourCtx.newJsonParser().encodeResourceToString(resp);
ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8";
ourResponseBody = respString;
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir");
List<IBaseResource> input = new ArrayList<IBaseResource>();
Patient p1 = new Patient(); // No ID
p1.addName().addFamily("PATIENT1");
input.add(p1);
Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2");
input.add(p2);
List<IBaseResource> response = client.transaction()
.withResources(input)
.encodedJson()
.execute();
assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri);
assertEquals(2, response.size());
String requestString = ourRequestBodyString;
ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString);
assertEquals(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod());
assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl());
p1 = (Patient) response.get(0);
assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified());
// assertEquals("PATIENT1", p1.getName().get(0).getFamily().get(0).getValue());
p2 = (Patient) response.get(1);
assertEquals(new IdDt("Patient/2/_history/2"), p2.getId().toUnqualified());
// assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue());
}
@Test
public void testTransactionWithString() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle();

View File

@ -22,11 +22,14 @@ package ca.uhn.fhir.rest.client.impl;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*;
@ -2046,7 +2049,35 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) {
Validate.notNull(theResources, "theResources must not be null");
return new TransactionExecutable<List<IBaseResource>>(theResources);
for (IBaseResource next : theResources) {
String entryMethod = null;
if (next instanceof IResource) {
BundleEntryTransactionMethodEnum entryMethodEnum = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) next);
if (entryMethodEnum != null) {
entryMethod = entryMethodEnum.getCode();
}
} else {
entryMethod = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IAnyResource) next);
}
if (isBlank(entryMethod)) {
if (isBlank(next.getIdElement().getValue())) {
entryMethod = "POST";
} else {
entryMethod = "PUT";
}
if (next instanceof IResource) {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put((IResource) next, BundleEntryTransactionMethodEnum.valueOf(entryMethod));
} else {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put((IAnyResource) next, entryMethod);
}
}
}
return new TransactionExecutable<>(theResources);
}
}

View File

@ -1722,7 +1722,7 @@ public class GenericJaxRsClientDstu2Test {
Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2");
p2.setId("http://example.com/Patient/2");
input.add(p2);
@ -1740,7 +1740,7 @@ public class GenericJaxRsClientDstu2Test {
assertEquals(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod());
assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl());
assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl());
p1 = (Patient) response.get(0);
assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified());

View File

@ -1758,7 +1758,7 @@ public class GenericJaxRsClientDstu3Test {
Patient p2 = new Patient(); // Yes ID
p2.addName().setFamily("PATIENT2");
p2.setId("Patient/2");
p2.setId("http://example.com/Patient/2");
input.add(p2);
//@formatter:off
@ -1776,7 +1776,7 @@ public class GenericJaxRsClientDstu3Test {
assertEquals(2, requestBundle.getEntry().size());
assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals(HTTPVerb.PUT, requestBundle.getEntry().get(1).getRequest().getMethod());
assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl());
assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl());
p1 = (Patient) response.get(0);
assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement());

View File

@ -42,14 +42,6 @@ public final class MetadataKeyCurrentlyReindexing extends ResourceMetadataKeySup
return (Boolean) theResource.getResourceMetadata().get(IDao.CURRENTLY_REINDEXING);
}
public Boolean get(IBaseResource theResource) {
if (theResource instanceof IAnyResource) {
return get((IAnyResource) theResource);
} else {
return get((IResource) theResource);
}
}
@Override
public void put(IAnyResource theResource, Boolean theObject) {
theResource.setUserData(IDao.CURRENTLY_REINDEXING.name(), theObject);

View File

@ -4,21 +4,20 @@ import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.util.TestUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
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.Observation.ObservationStatus;
@ -156,7 +155,22 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().create().resourcesOfType("Patient").withAnyId().withTester(new IAuthRuleTester() {
@Override
public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) {
if (theInputResource instanceof Patient) {
Patient patient = (Patient) theInputResource;
return patient
.getIdentifier()
.stream()
.filter(t-> "http://uhn.ca/mrns".equals(t.getSystem()))
.anyMatch(t-> "100".equals(t.getValue()));
}
return false;
}
}).andThen()
.allow().createConditional().resourcesOfType("Patient").andThen()
.allow().transaction().withAnyOperation().andApplyNormalRules().andThen()
.build();
}
});
@ -177,19 +191,16 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
Bundle response = ourClient.transaction().withBundle(request).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
try {
ourClient.update().resource(patient).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
// Subsequent calls also shouldn't fail
ourClient.transaction().withBundle(request).execute();
ourClient.transaction().withBundle(request).execute();
}
// Create a patient (blocked)
// Create a patient with wrong identifier (blocked)
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("101");
patient.addName().setFamily("Tester").addGiven("Fozzie");
Bundle request = new Bundle();
request.setType(Bundle.BundleType.TRANSACTION);
@ -197,12 +208,31 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
.setResource(patient)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|100");
Bundle response = ourClient.transaction().withBundle(request).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
.setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|101");
try {
ourClient.update().resource(patient).execute();
ourClient.transaction().withBundle(request).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
}
// Create an organization (blocked)
{
Organization patient = new Organization();
patient.setName("FOO");
Bundle request = new Bundle();
request.setType(Bundle.BundleType.TRANSACTION);
request.addEntry()
.setResource(patient)
.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setIfNoneExist("Organization?name=FOO");
try {
ourClient.transaction().withBundle(request).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());

View File

@ -73,6 +73,7 @@ public abstract class RequestDetails {
private Map<Object, Object> myUserData;
private IBaseResource myResource;
private String myRequestId;
private String myFixedConditionalUrl;
/**
* Constructor
@ -81,6 +82,14 @@ public abstract class RequestDetails {
myInterceptorBroadcaster = theInterceptorBroadcaster;
}
public String getFixedConditionalUrl() {
return myFixedConditionalUrl;
}
public void setFixedConditionalUrl(String theFixedConditionalUrl) {
myFixedConditionalUrl = theFixedConditionalUrl;
}
public String getRequestId() {
return myRequestId;
}
@ -152,6 +161,9 @@ public abstract class RequestDetails {
* @return Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise
*/
public String getConditionalUrl(RestOperationTypeEnum theOperationType) {
if (myFixedConditionalUrl != null) {
return myFixedConditionalUrl;
}
switch (theOperationType) {
case CREATE:
String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST);

View File

@ -108,8 +108,17 @@ public interface IAuthRuleBuilderRule {
*/
IAuthRuleBuilderRuleOp write();
/**
* This rule specifically allows a user to perform a FHIR create, but not an update or other write operations
*
* @see #write()
* @since 4.1.0
*/
IAuthRuleBuilderRuleOp create();
/**
* Allow a GraphQL query
*/
IAuthRuleBuilderGraphQL graphQL();
}

View File

@ -253,6 +253,14 @@ public class RuleBuilder implements IAuthRuleBuilder {
return myWriteRuleBuilder;
}
@Override
public IAuthRuleBuilderRuleOp create() {
if (myWriteRuleBuilder == null) {
myWriteRuleBuilder = new RuleBuilderRuleOp(RuleOpEnum.CREATE);
}
return myWriteRuleBuilder;
}
@Override
public IAuthRuleBuilderGraphQL graphQL() {
return new RuleBuilderGraphQL();

View File

@ -207,6 +207,19 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return null;
}
break;
case CREATE:
if (theInputResource == null && theInputResourceId == null) {
return null;
}
if (theOperation == RestOperationTypeEnum.CREATE) {
appliesToResource = theInputResource;
if (theInputResourceId != null) {
appliesToResourceId = Collections.singletonList(theInputResourceId);
}
} else {
return null;
}
break;
case DELETE:
if (theOperation == RestOperationTypeEnum.DELETE) {
if (myAppliesToDeleteCascade != (thePointcut == Pointcut.STORAGE_CASCADE_DELETE)) {
@ -288,7 +301,13 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
}
}
String previousFixedConditionalUrl = theRequestDetails.getFixedConditionalUrl();
theRequestDetails.setFixedConditionalUrl(nextPart.getConditionalUrl());
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, inputResourceId, null, thePointcut);
theRequestDetails.setFixedConditionalUrl(previousFixedConditionalUrl);
if (newVerdict == null) {
continue;
} else if (verdict == null) {

View File

@ -33,5 +33,6 @@ enum RuleOpEnum {
DELETE,
OPERATION,
GRAPHQL,
CREATE,
PATCH
}

View File

@ -51,90 +51,6 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<IBaseReference>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();

View File

@ -50,86 +50,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
IResource next = (IResource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
for (IResource nextContained : next.getContained().getContainedResources()) {
if (nextContained.getId().isEmpty() == false) {
containedIds.add(nextContained.getId().getValue());
}
}
List<BaseResourceReferenceDt> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
do {
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
for (BaseResourceReferenceDt nextRef : references) {
IResource nextRes = (IResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getId().hasIdPart()) {
if (containedIds.contains(nextRes.getId().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IdDt id = nextRes.getId();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<BaseResourceReferenceDt>();
for (IResource iResource : addedResourcesThisPass) {
List<BaseResourceReferenceDt> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
Entry entry = myBundle.addEntry().setResource(next);
if (next.getId().hasBaseUrl()) {
entry.setFullUrl(next.getId().getValue());
}
BundleEntryTransactionMethodEnum httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb.getCode());
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
Entry entry = myBundle.addEntry();
entry.setResource((IResource) next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();

View File

@ -2231,25 +2231,27 @@ public class GenericClientDstu2Test {
Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2");
p2.setId("http://foo.com/Patient/2");
input.add(p2);
//@formatter:off
List<IBaseResource> response = client.transaction()
.withResources(input)
.encodedJson()
.prettyPrint()
.execute();
//@formatter:on
assertEquals("http://example.com/fhir", capt.getValue().getURI().toString());
assertEquals("http://example.com/fhir?_pretty=true", capt.getValue().getURI().toString());
assertEquals(2, response.size());
String requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent());
ourLog.info(requestString);
ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString);
assertEquals(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod());
assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl());
assertEquals("http://foo.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl());
assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
p1 = (Patient) response.get(0);

View File

@ -51,93 +51,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<IBaseReference>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {

View File

@ -23,17 +23,16 @@ package ca.uhn.fhir.rest.server.provider.dstu2hl7org;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.dstu2.model.Bundle;
import org.hl7.fhir.dstu2.model.*;
import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode;
import org.hl7.fhir.dstu2.model.IdType;
import org.hl7.fhir.dstu2.model.InstantType;
import org.hl7.fhir.dstu2.model.Resource;
import org.hl7.fhir.instance.model.api.*;
import java.util.*;
@ -51,8 +50,12 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase,
BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
@ -61,22 +64,28 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
}
}
for (IBaseResource nextBaseRes : theResult) {
IDomainResource next = (IDomainResource) nextBaseRes;
for (IBaseResource next : theResult) {
Set<String> containedIds = new HashSet<String>();
for (IBaseResource nextContained : next.getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (isNotBlank(nextContained.getId())) {
containedIds.add(nextContained.getId());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next,
IBaseReference.class);
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
do {
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (IBaseReference nextRef : references) {
IBaseResource nextRes = (IBaseResource) nextRef.getResource();
for (ResourceReferenceInfo nextRefInfo : references) {
if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
continue;
}
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
@ -99,119 +108,62 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<IBaseReference>();
for (IBaseResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource,
IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
populateBundleEntryFullUrl(next, entry);
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
populateBundleEntryFullUrl(next, entry);
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase,
BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource next : theResult) {
List<? extends IAnyResource> contained;
if (next instanceof IDomainResource) {
IDomainResource nextDomain = (IDomainResource) next;
contained = nextDomain.getContained();
} else {
contained = Collections.emptyList();
}
Set<String> containedIds = new HashSet<String>();
for (IAnyResource nextContained : contained) {
if (nextContained.getId().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
do {
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
for (ResourceReferenceInfo nextRefInfo : references) {
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes))
continue;
IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IdType id = (IdType) nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
includedResources.addAll(addedResourcesThisPass);
// Linked resources may themselves have linked resources
references = new ArrayList<ResourceReferenceInfo>();
for (IBaseResource iResource : addedResourcesThisPass) {
references = new ArrayList<>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
references.addAll(newReferences);
}
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
populateBundleEntryFullUrl(next, entry);
Resource nextAsResource = (Resource) next;
IIdType id = populateBundleEntryFullUrl(next, entry);
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
if (id != null) {
entry.getRequest().setUrl(id.getValue());
}
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
// Populate Bundle.entry.response
if (theBundleType != null) {
switch (theBundleType) {
case BATCH_RESPONSE:
case TRANSACTION_RESPONSE:
if ("1".equals(id.getVersionIdPart())) {
entry.getResponse().setStatus("201 Created");
} else if (isNotBlank(id.getVersionIdPart())) {
entry.getResponse().setStatus("200 OK");
}
if (isNotBlank(id.getVersionIdPart())) {
entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart()));
}
break;
}
}
// Populate Bundle.entry.search
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode);
}
// BundleEntrySearchModeEnum searchMode =
// ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
// if (searchMode != null) {
// entry.getSearch().getModeElement().setValue(searchMode.getCode());
// }
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
for (IAnyResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
populateBundleEntryFullUrl(next, entry);
}
}
@ -228,7 +180,7 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null) {
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
InstantType instantType = new InstantType();
instantType.setValueAsString(theLastUpdated.getValueAsString());
myBundle.getMeta().setLastUpdatedElement(instantType);
@ -280,16 +232,19 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myBundle = (Bundle) theBundle;
}
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
IIdType idElement = null;
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().toVersionless().getValue());
idElement = next.getIdElement();
entry.setFullUrl(idElement.toVersionless().getValue());
} else {
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
IIdType id = next.getIdElement().toVersionless();
id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(id.getValue());
idElement = next.getIdElement();
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(idElement.toVersionless().getValue());
}
}
return idElement;
}
@Override

View File

@ -629,7 +629,7 @@ public class GenericClientDstu2Hl7OrgTest {
Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2");
p2.setId("http://example.com/Patient/2");
input.add(p2);
//@formatter:off
@ -647,7 +647,7 @@ public class GenericClientDstu2Hl7OrgTest {
assertEquals(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod().name());
assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod().name());
assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl());
assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl());
p1 = (Patient) response.get(0);
assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement().toUnqualified());

View File

@ -52,93 +52,6 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<>();
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();

View File

@ -52,93 +52,6 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<>();
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();

View File

@ -55,86 +55,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
IResource next = (IResource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
for (IResource nextContained : next.getContained().getContainedResources()) {
if (nextContained.getId().isEmpty() == false) {
containedIds.add(nextContained.getId().getValue());
}
}
List<BaseResourceReferenceDt> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
do {
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
for (BaseResourceReferenceDt nextRef : references) {
IResource nextRes = (IResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getId().hasIdPart()) {
if (containedIds.contains(nextRes.getId().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IdDt id = nextRes.getId();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<BaseResourceReferenceDt>();
for (IResource iResource : addedResourcesThisPass) {
List<BaseResourceReferenceDt> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
Entry entry = myBundle.addEntry().setResource(next);
if (next.getId().hasBaseUrl()) {
entry.setFullUrl(next.getId().getValue());
}
BundleEntryTransactionMethodEnum httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb.getCode());
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
Entry entry = myBundle.addEntry();
entry.setResource((IResource) next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
if (myBundle == null) {

View File

@ -281,6 +281,12 @@
tables. In particular, this was added to facilitate the LOINC EXTERNAL_COPYRIGHT_NOTICE property, for which
values can be quite long.
</action>
<action type="add">
The AuthorizationInterceptor has been enhanced so that a user can be authorized to
perform create operations specifically, without authorizing all write operations. Also,
conditional creates can now be authorized even if they are happening inside a FHIR
transaction.
</action>
</release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">