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 ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource; 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 org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.io.Serializable; 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 RequestTypeEnum myRequestType;
private final IBaseResource myResource; private final IBaseResource myResource;
private final String myUrl; 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(); super();
myRequestType = theRequestType; myRequestType = theRequestType;
myUrl = theUrl; myUrl = theUrl;
myResource = theResource; myResource = theResource;
myConditionalUrl = theConditionalUrl;
} }
public RequestTypeEnum getRequestType() { public RequestTypeEnum getRequestType() {
@ -62,6 +64,10 @@ public class BundleUtil {
return myResource; return myResource;
} }
public String getConditionalUrl() {
return myConditionalUrl;
}
public String getUrl() { public String getUrl() {
return myUrl; return myUrl;
} }
@ -190,19 +196,21 @@ public class BundleUtil {
BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource"); BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request"); BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request");
BaseRuntimeElementCompositeDefinition<?> requestElem = (BaseRuntimeElementCompositeDefinition<?>) requestChild.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"); BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method");
for (IBase nextEntry : entries) { for (IBase nextEntry : entries) {
IBaseResource resource = null; IBaseResource resource = null;
String url = null; String url = null;
RequestTypeEnum requestType = null; RequestTypeEnum requestType = null;
String conditionalUrl = null;
for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) { for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
resource = (IBaseResource) next; resource = (IBaseResource) next;
} }
for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) { 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(); url = ((IPrimitiveType<?>) nextUrl).getValueAsString();
} }
for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) { for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) {
@ -211,13 +219,29 @@ public class BundleUtil {
requestType = RequestTypeEnum.valueOf(methodString); 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 * All 3 might be null - That's ok because we still want to know the
* order in the original bundle. * 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()); 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 @Test
public void testTransactionWithString() throws Exception { public void testTransactionWithString() throws Exception {
ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle(); 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.context.*;
import ca.uhn.fhir.model.api.IQueryParameterType; 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.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt; 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.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.*;
@ -2046,7 +2049,35 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override @Override
public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) { public ITransactionTyped<List<IBaseResource>> withResources(List<? extends IBaseResource> theResources) {
Validate.notNull(theResources, "theResources must not be null"); 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 Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2"); p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2"); p2.setId("http://example.com/Patient/2");
input.add(p2); input.add(p2);
@ -1740,7 +1740,7 @@ public class GenericJaxRsClientDstu2Test {
assertEquals(2, requestBundle.getEntry().size()); assertEquals(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals("PUT", requestBundle.getEntry().get(1).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); p1 = (Patient) response.get(0);
assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified()); 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 Patient p2 = new Patient(); // Yes ID
p2.addName().setFamily("PATIENT2"); p2.addName().setFamily("PATIENT2");
p2.setId("Patient/2"); p2.setId("http://example.com/Patient/2");
input.add(p2); input.add(p2);
//@formatter:off //@formatter:off
@ -1776,7 +1776,7 @@ public class GenericJaxRsClientDstu3Test {
assertEquals(2, requestBundle.getEntry().size()); assertEquals(2, requestBundle.getEntry().size());
assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals(HTTPVerb.PUT, requestBundle.getEntry().get(1).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); p1 = (Patient) response.get(0);
assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement()); 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); 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 @Override
public void put(IAnyResource theResource, Boolean theObject) { public void put(IAnyResource theResource, Boolean theObject) {
theResource.setUserData(IDao.CURRENTLY_REINDEXING.name(), 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.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome; 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.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; 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.*;
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.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity; 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.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.Observation.ObservationStatus;
@ -156,7 +155,22 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
@Override @Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder() 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().createConditional().resourcesOfType("Patient").andThen()
.allow().transaction().withAnyOperation().andApplyNormalRules().andThen()
.build(); .build();
} }
}); });
@ -177,19 +191,16 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
Bundle response = ourClient.transaction().withBundle(request).execute(); Bundle response = ourClient.transaction().withBundle(request).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response)); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
try { // Subsequent calls also shouldn't fail
ourClient.update().resource(patient).execute(); ourClient.transaction().withBundle(request).execute();
fail(); ourClient.transaction().withBundle(request).execute();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
} }
// Create a patient (blocked) // Create a patient with wrong identifier (blocked)
{ {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("101");
patient.addName().setFamily("Tester").addGiven("Raghad"); patient.addName().setFamily("Tester").addGiven("Fozzie");
Bundle request = new Bundle(); Bundle request = new Bundle();
request.setType(Bundle.BundleType.TRANSACTION); request.setType(Bundle.BundleType.TRANSACTION);
@ -197,12 +208,31 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
.setResource(patient) .setResource(patient)
.getRequest() .getRequest()
.setMethod(Bundle.HTTPVerb.POST) .setMethod(Bundle.HTTPVerb.POST)
.setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|100"); .setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|101");
Bundle response = ourClient.transaction().withBundle(request).execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
try { 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(); fail();
} catch (ForbiddenOperationException e) { } catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); 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 Map<Object, Object> myUserData;
private IBaseResource myResource; private IBaseResource myResource;
private String myRequestId; private String myRequestId;
private String myFixedConditionalUrl;
/** /**
* Constructor * Constructor
@ -81,6 +82,14 @@ public abstract class RequestDetails {
myInterceptorBroadcaster = theInterceptorBroadcaster; myInterceptorBroadcaster = theInterceptorBroadcaster;
} }
public String getFixedConditionalUrl() {
return myFixedConditionalUrl;
}
public void setFixedConditionalUrl(String theFixedConditionalUrl) {
myFixedConditionalUrl = theFixedConditionalUrl;
}
public String getRequestId() { public String getRequestId() {
return myRequestId; 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 * @return Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise
*/ */
public String getConditionalUrl(RestOperationTypeEnum theOperationType) { public String getConditionalUrl(RestOperationTypeEnum theOperationType) {
if (myFixedConditionalUrl != null) {
return myFixedConditionalUrl;
}
switch (theOperationType) { switch (theOperationType) {
case CREATE: case CREATE:
String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST); String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST);

View File

@ -108,8 +108,17 @@ public interface IAuthRuleBuilderRule {
*/ */
IAuthRuleBuilderRuleOp write(); 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 * Allow a GraphQL query
*/ */
IAuthRuleBuilderGraphQL graphQL(); IAuthRuleBuilderGraphQL graphQL();
} }

View File

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

View File

@ -207,6 +207,19 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return null; return null;
} }
break; 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: case DELETE:
if (theOperation == RestOperationTypeEnum.DELETE) { if (theOperation == RestOperationTypeEnum.DELETE) {
if (myAppliesToDeleteCascade != (thePointcut == Pointcut.STORAGE_CASCADE_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); Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, inputResourceId, null, thePointcut);
theRequestDetails.setFixedConditionalUrl(previousFixedConditionalUrl);
if (newVerdict == null) { if (newVerdict == null) {
continue; continue;
} else if (verdict == null) { } else if (verdict == null) {

View File

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

View File

@ -51,90 +51,6 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle(); ensureBundle();

View File

@ -50,86 +50,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle(); ensureBundle();

View File

@ -2231,25 +2231,27 @@ public class GenericClientDstu2Test {
Patient p2 = new Patient(); // Yes ID Patient p2 = new Patient(); // Yes ID
p2.addName().addFamily("PATIENT2"); p2.addName().addFamily("PATIENT2");
p2.setId("Patient/2"); p2.setId("http://foo.com/Patient/2");
input.add(p2); input.add(p2);
//@formatter:off //@formatter:off
List<IBaseResource> response = client.transaction() List<IBaseResource> response = client.transaction()
.withResources(input) .withResources(input)
.encodedJson() .encodedJson()
.prettyPrint()
.execute(); .execute();
//@formatter:on //@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()); assertEquals(2, response.size());
String requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); 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); 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(2, requestBundle.getEntry().size());
assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod());
assertEquals("PUT", requestBundle.getEntry().get(1).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(";.*", "")); assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
p1 = (Patient) response.get(0); p1 = (Patient) response.get(0);

View File

@ -51,93 +51,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { 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.FhirContext;
import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include; 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.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.util.ResourceReferenceInfo; 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.BundleEntryComponent;
import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent; import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode; 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 org.hl7.fhir.instance.model.api.*;
import java.util.*; import java.util.*;
@ -51,8 +50,12 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; myContext = theContext;
} }
private void addResourcesForSearch(List<? extends IBaseResource> theResult) { @Override
List<IBaseResource> includedResources = new ArrayList<IBaseResource>(); 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>(); Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) { for (IBaseResource next : theResult) {
@ -61,22 +64,28 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
} }
} }
for (IBaseResource nextBaseRes : theResult) { for (IBaseResource next : theResult) {
IDomainResource next = (IDomainResource) nextBaseRes;
Set<String> containedIds = new HashSet<String>(); Set<String> containedIds = new HashSet<String>();
for (IBaseResource nextContained : next.getContained()) {
if (nextContained.getIdElement().isEmpty() == false) { if (next instanceof DomainResource) {
containedIds.add(nextContained.getIdElement().getValue()); for (Resource nextContained : ((DomainResource) next).getContained()) {
if (isNotBlank(nextContained.getId())) {
containedIds.add(nextContained.getId());
}
} }
} }
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
IBaseReference.class);
do { do {
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>(); List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (IBaseReference nextRef : references) { for (ResourceReferenceInfo nextRefInfo : references) {
IBaseResource nextRes = (IBaseResource) nextRef.getResource(); if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
continue;
}
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) { if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) { if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) { 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); includedResources.addAll(addedResourcesThisPass);
// Linked resources may themselves have linked resources // Linked resources may themselves have linked resources
references = new ArrayList<ResourceReferenceInfo>(); references = new ArrayList<>();
for (IBaseResource iResource : addedResourcesThisPass) { for (IAnyResource iResource : addedResourcesThisPass) {
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource); List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
references.addAll(newReferences); references.addAll(newReferences);
} }
} while (references.isEmpty() == false); } while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); 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 * Actually add the resources to the bundle
*/ */
for (IBaseResource next : includedResources) { for (IAnyResource next : includedResources) {
myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); 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()); myBundle.setId(UUID.randomUUID().toString());
} }
if (myBundle.getMeta().getLastUpdated() == null) { if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
InstantType instantType = new InstantType(); InstantType instantType = new InstantType();
instantType.setValueAsString(theLastUpdated.getValueAsString()); instantType.setValueAsString(theLastUpdated.getValueAsString());
myBundle.getMeta().setLastUpdatedElement(instantType); myBundle.getMeta().setLastUpdatedElement(instantType);
@ -280,16 +232,19 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
myBundle = (Bundle) theBundle; myBundle = (Bundle) theBundle;
} }
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
IIdType idElement = null;
if (next.getIdElement().hasBaseUrl()) { if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().toVersionless().getValue()); idElement = next.getIdElement();
entry.setFullUrl(idElement.toVersionless().getValue());
} else { } else {
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
IIdType id = next.getIdElement().toVersionless(); idElement = next.getIdElement();
id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(id.getValue()); entry.setFullUrl(idElement.toVersionless().getValue());
} }
} }
return idElement;
} }
@Override @Override

View File

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

View File

@ -52,93 +52,6 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle(); ensureBundle();

View File

@ -52,93 +52,6 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle(); ensureBundle();

View File

@ -55,86 +55,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext; 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 @Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) { public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
if (myBundle == null) { 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 tables. In particular, this was added to facilitate the LOINC EXTERNAL_COPYRIGHT_NOTICE property, for which
values can be quite long. values can be quite long.
</action> </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>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)"> <release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix"> <action type="fix">