Fix #363 - Allow remote references in JPA resources
This commit is contained in:
parent
b03bea3a58
commit
3d8776f6ed
|
@ -48,7 +48,8 @@ ca.uhn.fhir.validation.ValidationResult.noIssuesDetected=No issues detected duri
|
||||||
|
|
||||||
# JPA Messages
|
# JPA Messages
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request.
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.externalReferenceNotAllowed=Resource contains external reference to URL "{0}" but this server is not configured to allow external references
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlInvalidResourceType=Invalid match URL "{0}" - Unknown resource type: "{1}"
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlInvalidResourceType=Invalid match URL "{0}" - Unknown resource type: "{1}"
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlNoMatches=Invalid match URL "{0}" - No resources match this search
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlNoMatches=Invalid match URL "{0}" - No resources match this search
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlMultipleMatches=Invalid match URL "{0}" - Multiple resources match this search
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlMultipleMatches=Invalid match URL "{0}" - Multiple resources match this search
|
||||||
|
|
|
@ -243,14 +243,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Set<ResourceLink> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource) {
|
protected void extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks) {
|
||||||
Set<ResourceLink> retVal = new HashSet<ResourceLink>();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
|
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
|
||||||
*/
|
*/
|
||||||
if (theResource instanceof IBaseBundle) {
|
if (theResource instanceof IBaseBundle) {
|
||||||
return retVal;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||||
|
@ -274,7 +273,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
for (PathAndRef nextPathAndRef : refs) {
|
for (PathAndRef nextPathAndRef : refs) {
|
||||||
Object nextObject = nextPathAndRef.getRef();
|
Object nextObject = nextPathAndRef.getRef();
|
||||||
|
|
||||||
ResourceLink nextEntity;
|
|
||||||
IIdType nextId;
|
IIdType nextId;
|
||||||
if (nextObject instanceof IBaseReference) {
|
if (nextObject instanceof IBaseReference) {
|
||||||
IBaseReference nextValue = (IBaseReference) nextObject;
|
IBaseReference nextValue = (IBaseReference) nextObject;
|
||||||
|
@ -304,6 +302,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String baseUrl = nextId.getBaseUrl();
|
||||||
String typeString = nextId.getResourceType();
|
String typeString = nextId.getResourceType();
|
||||||
if (isBlank(typeString)) {
|
if (isBlank(typeString)) {
|
||||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextId.getValue());
|
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextId.getValue());
|
||||||
|
@ -316,6 +315,18 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextId.getValue());
|
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextId.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isNotBlank(baseUrl)) {
|
||||||
|
if (!getConfig().getTreatBaseUrlsAsLocal().contains(baseUrl) && !getConfig().isAllowExternalReferences()) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "externalReferenceNotAllowed", nextId.getValue());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
} else {
|
||||||
|
if (theLinks.add(new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId))) {
|
||||||
|
ourLog.info("Indexing remote resource reference URL: {}", nextId);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
|
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
|
||||||
String id = nextId.getIdPart();
|
String id = nextId.getIdPart();
|
||||||
if (StringUtils.isBlank(id)) {
|
if (StringUtils.isBlank(id)) {
|
||||||
|
@ -357,45 +368,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /*
|
theLinks.add(new ResourceLink(nextPathAndRef.getPath(), theEntity, target));
|
||||||
// * Is the target type an allowable type of resource for the path where it is referenced?
|
|
||||||
// */
|
|
||||||
//
|
|
||||||
// if (allowedTypesInField == null) {
|
|
||||||
// BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPathAndRef.getPath());
|
|
||||||
// if (childDef instanceof RuntimeChildResourceDefinition) {
|
|
||||||
// RuntimeChildResourceDefinition resRefDef = (RuntimeChildResourceDefinition) childDef;
|
|
||||||
// allowedTypesInField = resRefDef.getResourceTypes();
|
|
||||||
// } else {
|
|
||||||
// allowedTypesInField = new ArrayList<Class<? extends IBaseResource>>();
|
|
||||||
// allowedTypesInField.add(IBaseResource.class);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// boolean acceptableLink = false;
|
|
||||||
// for (Class<? extends IBaseResource> next : allowedTypesInField) {
|
|
||||||
// if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) {
|
|
||||||
// acceptableLink = true;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!acceptableLink) {
|
|
||||||
// throw new UnprocessableEntityException(
|
|
||||||
// "Invalid reference found at path '" + nextPathAndRef.getPath() + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path");
|
|
||||||
// }
|
|
||||||
|
|
||||||
nextEntity = new ResourceLink(nextPathAndRef.getPath(), theEntity, target);
|
|
||||||
if (nextEntity != null) {
|
|
||||||
retVal.add(nextEntity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
theEntity.setHasLinks(retVal.size() > 0);
|
theEntity.setHasLinks(theLinks.size() > 0);
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
|
@ -1268,7 +1247,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
links = extractResourceLinks(theEntity, theResource);
|
links = new HashSet<ResourceLink>();
|
||||||
|
extractResourceLinks(theEntity, theResource, links);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
||||||
|
|
|
@ -88,6 +88,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.ObjectUtil;
|
import ca.uhn.fhir.util.ObjectUtil;
|
||||||
|
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends BaseHapiFhirDao<T> implements IFhirResourceDao<T> {
|
public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends BaseHapiFhirDao<T> implements IFhirResourceDao<T> {
|
||||||
|
@ -708,6 +709,24 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithInvalidId", theResource.getIdElement().getIdPart()));
|
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithInvalidId", theResource.getIdElement().getIdPart()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Replace absolute references with relative ones if configured to
|
||||||
|
* do so
|
||||||
|
*/
|
||||||
|
if (getConfig().getTreatBaseUrlsAsLocal().isEmpty() == false) {
|
||||||
|
FhirTerser t = getContext().newTerser();
|
||||||
|
List<ResourceReferenceInfo> refs = t.getAllResourceReferences(theResource);
|
||||||
|
for (ResourceReferenceInfo nextRef : refs) {
|
||||||
|
IIdType refId = nextRef.getResourceReference().getReferenceElement();
|
||||||
|
if (refId != null && refId.hasBaseUrl()) {
|
||||||
|
if (getConfig().getTreatBaseUrlsAsLocal().contains(refId.getBaseUrl())) {
|
||||||
|
IIdType newRefId = refId.toUnqualified();
|
||||||
|
nextRef.getResourceReference().setReference(newRefId.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,8 +3,11 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
|
||||||
|
@ -34,19 +37,24 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
public class DaoConfig {
|
public class DaoConfig {
|
||||||
|
|
||||||
|
// ***
|
||||||
|
// update setter javadoc if default changes
|
||||||
|
// ***
|
||||||
|
private boolean myAllowExternalReferences = false;
|
||||||
|
|
||||||
// ***
|
// ***
|
||||||
// update setter javadoc if default changes
|
// update setter javadoc if default changes
|
||||||
// ***
|
// ***
|
||||||
private boolean myAllowInlineMatchUrlReferences = false;
|
private boolean myAllowInlineMatchUrlReferences = false;
|
||||||
|
|
||||||
private boolean myAllowMultipleDelete;
|
private boolean myAllowMultipleDelete;
|
||||||
|
|
||||||
// ***
|
// ***
|
||||||
// update setter javadoc if default changes
|
// update setter javadoc if default changes
|
||||||
// ***
|
// ***
|
||||||
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
|
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
|
||||||
private int myHardSearchLimit = 1000;
|
private int myHardSearchLimit = 1000;
|
||||||
private int myHardTagListLimit = 1000;
|
private int myHardTagListLimit = 1000;
|
||||||
|
|
||||||
private int myIncludeLimit = 2000;
|
private int myIncludeLimit = 2000;
|
||||||
|
|
||||||
// ***
|
// ***
|
||||||
|
@ -55,39 +63,49 @@ public class DaoConfig {
|
||||||
private boolean myIndexContainedResources = true;
|
private boolean myIndexContainedResources = true;
|
||||||
|
|
||||||
private List<IServerInterceptor> myInterceptors;
|
private List<IServerInterceptor> myInterceptors;
|
||||||
|
|
||||||
// ***
|
// ***
|
||||||
// update setter javadoc if default changes
|
// update setter javadoc if default changes
|
||||||
// ***
|
// ***
|
||||||
private int myMaximumExpansionSize = 5000;
|
private int myMaximumExpansionSize = 5000;
|
||||||
|
|
||||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||||
|
|
||||||
private boolean mySchedulingDisabled;
|
private boolean mySchedulingDisabled;
|
||||||
|
|
||||||
private boolean mySubscriptionEnabled;
|
private boolean mySubscriptionEnabled;
|
||||||
|
|
||||||
private long mySubscriptionPollDelay = 1000;
|
private long mySubscriptionPollDelay = 1000;
|
||||||
|
|
||||||
private Long mySubscriptionPurgeInactiveAfterMillis;
|
private Long mySubscriptionPurgeInactiveAfterMillis;
|
||||||
|
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search results are stored in the database so that they can be paged through. After this
|
* Sets the number of milliseconds that search results for a given client search
|
||||||
* number of milliseconds, they will be deleted from the database. Defaults to 1 hour.
|
* should be preserved before being purged from the database.
|
||||||
|
* <p>
|
||||||
|
* Search results are stored in the database so that they can be paged over multiple
|
||||||
|
* requests. After this
|
||||||
|
* number of milliseconds, they will be deleted from the database, and any paging links
|
||||||
|
* (next/prev links in search response bundles) will become invalid. Defaults to 1 hour.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
public long getExpireSearchResultsAfterMillis() {
|
public long getExpireSearchResultsAfterMillis() {
|
||||||
return myExpireSearchResultsAfterMillis;
|
return myExpireSearchResultsAfterMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link #setIncludeLimit(int)}
|
* Gets the maximum number of results to return in a GetTags query (DSTU1 only)
|
||||||
*/
|
*/
|
||||||
public int getHardSearchLimit() {
|
|
||||||
return myHardSearchLimit;
|
|
||||||
}
|
|
||||||
public int getHardTagListLimit() {
|
public int getHardTagListLimit() {
|
||||||
return myHardTagListLimit;
|
return myHardTagListLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIncludeLimit() {
|
public int getIncludeLimit() {
|
||||||
return myIncludeLimit;
|
return myIncludeLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the interceptors which will be notified of operations.
|
* Returns the interceptors which will be notified of operations.
|
||||||
*
|
*
|
||||||
|
@ -111,11 +129,51 @@ public class DaoConfig {
|
||||||
public long getSubscriptionPollDelay() {
|
public long getSubscriptionPollDelay() {
|
||||||
return mySubscriptionPollDelay;
|
return mySubscriptionPollDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getSubscriptionPurgeInactiveAfterMillis() {
|
public Long getSubscriptionPurgeInactiveAfterMillis() {
|
||||||
return mySubscriptionPurgeInactiveAfterMillis;
|
return mySubscriptionPurgeInactiveAfterMillis;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This setting may be used to advise the server that any references found in
|
||||||
|
* resources that have any of the base URLs given here will be replaced with
|
||||||
|
* simple local references.
|
||||||
|
* <p>
|
||||||
|
* For example, if the set contains the value <code>http://example.com/base/</code>
|
||||||
|
* and a resource is submitted to the server that contains a reference to
|
||||||
|
* <code>http://example.com/base/Patient/1</code>, the server will automatically
|
||||||
|
* convert this reference to <code>Patient/1</code>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Set<String> getTreatBaseUrlsAsLocal() {
|
||||||
|
return myTreatBaseUrlsAsLocal;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
||||||
|
* resources to have references to external servers. For example if this server is
|
||||||
|
* running at <code>http://example.com/fhir</code> and this setting is set to
|
||||||
|
* <code>true</code> the server will allow a Patient resource to be saved with a
|
||||||
|
* Patient.organization value of <code>http://foo.com/Organization/1</code>.
|
||||||
|
* <p>
|
||||||
|
* Under the default behaviour if this value has not been changed, the above
|
||||||
|
* resource would be rejected by the server because it requires all references
|
||||||
|
* to be resolvable on the local server.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Note that external references will be indexed by the server and may be searched
|
||||||
|
* (e.g. <code>Patient:organization</code>), but
|
||||||
|
* chained searches (e.g. <code>Patient:organization.name</code>) will not work across
|
||||||
|
* these references.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
|
||||||
|
* is set to <code>true</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see #setTreatBaseUrlsAsLocal(Set)
|
||||||
|
* @see #setAllowExternalReferences(boolean)
|
||||||
|
*/
|
||||||
|
public boolean isAllowExternalReferences() {
|
||||||
|
return myAllowExternalReferences;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @see #setAllowInlineMatchUrlReferences(boolean)
|
* @see #setAllowInlineMatchUrlReferences(boolean)
|
||||||
*/
|
*/
|
||||||
|
@ -146,6 +204,35 @@ public class DaoConfig {
|
||||||
return mySubscriptionEnabled;
|
return mySubscriptionEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
||||||
|
* resources to have references to external servers. For example if this server is
|
||||||
|
* running at <code>http://example.com/fhir</code> and this setting is set to
|
||||||
|
* <code>true</code> the server will allow a Patient resource to be saved with a
|
||||||
|
* Patient.organization value of <code>http://foo.com/Organization/1</code>.
|
||||||
|
* <p>
|
||||||
|
* Under the default behaviour if this value has not been changed, the above
|
||||||
|
* resource would be rejected by the server because it requires all references
|
||||||
|
* to be resolvable on the local server.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Note that external references will be indexed by the server and may be searched
|
||||||
|
* (e.g. <code>Patient:organization</code>), but
|
||||||
|
* chained searches (e.g. <code>Patient:organization.name</code>) will not work across
|
||||||
|
* these references.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* It is recommended to also set {@link #setTreatBaseUrlsAsLocal(Set)} if this value
|
||||||
|
* is set to <code>true</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see #setTreatBaseUrlsAsLocal(Set)
|
||||||
|
* @see #setAllowExternalReferences(boolean)
|
||||||
|
*/
|
||||||
|
public void setAllowExternalReferences(boolean theAllowExternalReferences) {
|
||||||
|
myAllowExternalReferences = theAllowExternalReferences;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should references containing match URLs be resolved and replaced in create and update operations. For
|
* Should references containing match URLs be resolved and replaced in create and update operations. For
|
||||||
* example, if this property is set to true and a resource is created containing a reference
|
* example, if this property is set to true and a resource is created containing a reference
|
||||||
|
@ -165,8 +252,14 @@ public class DaoConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search results are stored in the database so that they can be paged through. After this
|
* Sets the number of milliseconds that search results for a given client search
|
||||||
* number of milliseconds, they will be deleted from the database. Defaults to 1 hour.
|
* should be preserved before being purged from the database.
|
||||||
|
* <p>
|
||||||
|
* Search results are stored in the database so that they can be paged over multiple
|
||||||
|
* requests. After this
|
||||||
|
* number of milliseconds, they will be deleted from the database, and any paging links
|
||||||
|
* (next/prev links in search response bundles) will become invalid. Defaults to 1 hour.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
|
@ -178,6 +271,9 @@ public class DaoConfig {
|
||||||
myHardSearchLimit = theHardSearchLimit;
|
myHardSearchLimit = theHardSearchLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum number of results to return in a GetTags query (DSTU1 only)
|
||||||
|
*/
|
||||||
public void setHardTagListLimit(int theHardTagListLimit) {
|
public void setHardTagListLimit(int theHardTagListLimit) {
|
||||||
myHardTagListLimit = theHardTagListLimit;
|
myHardTagListLimit = theHardTagListLimit;
|
||||||
}
|
}
|
||||||
|
@ -246,8 +342,8 @@ public class DaoConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this server support subscription? If set to true, the server will enable the subscription monitoring mode,
|
* If set to true, the server will enable support for subscriptions. Subscriptions
|
||||||
* which adds a bit of overhead. Note that if this is enabled, you must also include Spring task scanning to your XML
|
* will by default be handled via a polling task. Note that if this is enabled, you must also include Spring task scanning to your XML
|
||||||
* config for the scheduled tasks used by the subscription module.
|
* config for the scheduled tasks used by the subscription module.
|
||||||
*/
|
*/
|
||||||
public void setSubscriptionEnabled(boolean theSubscriptionEnabled) {
|
public void setSubscriptionEnabled(boolean theSubscriptionEnabled) {
|
||||||
|
@ -269,4 +365,29 @@ public class DaoConfig {
|
||||||
setSubscriptionPurgeInactiveAfterMillis(theSeconds * DateUtils.MILLIS_PER_SECOND);
|
setSubscriptionPurgeInactiveAfterMillis(theSeconds * DateUtils.MILLIS_PER_SECOND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting may be used to advise the server that any references found in
|
||||||
|
* resources that have any of the base URLs given here will be replaced with
|
||||||
|
* simple local references.
|
||||||
|
* <p>
|
||||||
|
* For example, if the set contains the value <code>http://example.com/base/</code>
|
||||||
|
* and a resource is submitted to the server that contains a reference to
|
||||||
|
* <code>http://example.com/base/Patient/1</code>, the server will automatically
|
||||||
|
* convert this reference to <code>Patient/1</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theTreatBaseUrlsAsLocal The set of base URLs. May be <code>null</code>, which
|
||||||
|
* means no references will be treated as external
|
||||||
|
*/
|
||||||
|
public void setTreatBaseUrlsAsLocal(Set<String> theTreatBaseUrlsAsLocal) {
|
||||||
|
HashSet<String> treatBaseUrlsAsLocal = new HashSet<String>();
|
||||||
|
for (String next : ObjectUtils.defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet<String>())) {
|
||||||
|
while (next.endsWith("/")) {
|
||||||
|
next = next.substring(0, next.length() - 1);
|
||||||
|
}
|
||||||
|
treatBaseUrlsAsLocal.add(next);
|
||||||
|
}
|
||||||
|
myTreatBaseUrlsAsLocal = treatBaseUrlsAsLocal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashBasedTable;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
@ -577,9 +578,26 @@ public class SearchBuilder {
|
||||||
ReferenceParam ref = (ReferenceParam) params;
|
ReferenceParam ref = (ReferenceParam) params;
|
||||||
|
|
||||||
if (isBlank(ref.getChain())) {
|
if (isBlank(ref.getChain())) {
|
||||||
String resourceId = ref.getValueAsQueryToken(myContext);
|
IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null);
|
||||||
IIdType dt = new IdDt(resourceId);
|
|
||||||
List<Long> targetPid = myCallingDao.translateForcedIdToPids(dt);
|
if (dt.hasBaseUrl()) {
|
||||||
|
if (myCallingDao.getConfig().getTreatBaseUrlsAsLocal().contains(dt.getBaseUrl())) {
|
||||||
|
dt = dt.toUnqualified();
|
||||||
|
} else {
|
||||||
|
ourLog.debug("Searching for resource link with target URL: {}", dt.getValue());
|
||||||
|
Predicate eq = builder.equal(from.get("myTargetResourceUrl"), dt.getValue());
|
||||||
|
codePredicates.add(eq);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> targetPid;
|
||||||
|
try {
|
||||||
|
targetPid = myCallingDao.translateForcedIdToPids(dt);
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
doSetPids(new ArrayList<Long>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (Long next : targetPid) {
|
for (Long next : targetPid) {
|
||||||
ourLog.debug("Searching for resource link with target PID: {}", next);
|
ourLog.debug("Searching for resource link with target PID: {}", next);
|
||||||
Predicate eq = builder.equal(from.get("myTargetResourcePid"), next);
|
Predicate eq = builder.equal(from.get("myTargetResourcePid"), next);
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.hibernate.annotations.ColumnDefault;
|
import org.hibernate.annotations.ColumnDefault;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_RES_LINK" , indexes= {
|
@Table(name = "HFJ_RES_LINK" , indexes= {
|
||||||
|
@ -70,11 +71,11 @@ public class ResourceLink implements Serializable {
|
||||||
@Field()
|
@Field()
|
||||||
private String mySourceResourceType;
|
private String mySourceResourceType;
|
||||||
|
|
||||||
@ManyToOne(optional = false, fetch=FetchType.LAZY)
|
@ManyToOne(optional = true, fetch=FetchType.LAZY)
|
||||||
@JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false)
|
@JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = true)
|
||||||
private ResourceTable myTargetResource;
|
private ResourceTable myTargetResource;
|
||||||
|
|
||||||
@Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
|
@Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false, nullable = true)
|
||||||
@Field()
|
@Field()
|
||||||
private Long myTargetResourcePid;
|
private Long myTargetResourcePid;
|
||||||
|
|
||||||
|
@ -83,6 +84,10 @@ public class ResourceLink implements Serializable {
|
||||||
@Field()
|
@Field()
|
||||||
private String myTargetResourceType;
|
private String myTargetResourceType;
|
||||||
|
|
||||||
|
@Column(name = "TARGET_RESOURCE_URL", length=200, nullable = true)
|
||||||
|
@Field()
|
||||||
|
private String myTargetResourceUrl;
|
||||||
|
|
||||||
public ResourceLink() {
|
public ResourceLink() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -94,6 +99,13 @@ public class ResourceLink implements Serializable {
|
||||||
setTargetResource(theTargetResource);
|
setTargetResource(theTargetResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResourceLink(String theSourcePath, ResourceTable theSourceResource, IIdType theTargetResourceUrl) {
|
||||||
|
super();
|
||||||
|
setSourcePath(theSourcePath);
|
||||||
|
setSourceResource(theSourceResource);
|
||||||
|
setTargetResourceUrl(theTargetResourceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theObj) {
|
public boolean equals(Object theObj) {
|
||||||
if (this == theObj) {
|
if (this == theObj) {
|
||||||
|
@ -110,6 +122,7 @@ public class ResourceLink implements Serializable {
|
||||||
b.append(mySourcePath, obj.mySourcePath);
|
b.append(mySourcePath, obj.mySourcePath);
|
||||||
b.append(mySourceResource, obj.mySourceResource);
|
b.append(mySourceResource, obj.mySourceResource);
|
||||||
b.append(myTargetResourcePid, obj.myTargetResourcePid);
|
b.append(myTargetResourcePid, obj.myTargetResourcePid);
|
||||||
|
b.append(myTargetResourceUrl, obj.myTargetResourceUrl);
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,12 +146,17 @@ public class ResourceLink implements Serializable {
|
||||||
return myTargetResourcePid;
|
return myTargetResourcePid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getTargetResourceUrl() {
|
||||||
|
return myTargetResourceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
b.append(mySourcePath);
|
b.append(mySourcePath);
|
||||||
b.append(mySourceResource);
|
b.append(mySourceResource);
|
||||||
b.append(myTargetResourcePid);
|
b.append(myTargetResourcePid);
|
||||||
|
b.append(myTargetResourceUrl);
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +177,15 @@ public class ResourceLink implements Serializable {
|
||||||
myTargetResourceType = theTargetResource.getResourceType();
|
myTargetResourceType = theTargetResource.getResourceType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTargetResourceUrl(IIdType theTargetResourceUrl) {
|
||||||
|
Validate.isTrue(theTargetResourceUrl.hasBaseUrl());
|
||||||
|
Validate.isTrue(theTargetResourceUrl.hasResourceType());
|
||||||
|
Validate.isTrue(theTargetResourceUrl.hasIdPart());
|
||||||
|
|
||||||
|
myTargetResourceType = theTargetResourceUrl.getResourceType();
|
||||||
|
myTargetResourceUrl = theTargetResourceUrl.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
|
@ -166,6 +193,7 @@ public class ResourceLink implements Serializable {
|
||||||
b.append("path=").append(mySourcePath);
|
b.append("path=").append(mySourcePath);
|
||||||
b.append(", src=").append(mySourceResourcePid);
|
b.append(", src=").append(mySourceResourcePid);
|
||||||
b.append(", target=").append(myTargetResourcePid);
|
b.append(", target=").append(myTargetResourcePid);
|
||||||
|
b.append(", targetUrl=").append(myTargetResourceUrl);
|
||||||
|
|
||||||
b.append("]");
|
b.append("]");
|
||||||
return b.toString();
|
return b.toString();
|
||||||
|
|
|
@ -149,8 +149,8 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
private boolean myHasLinks;
|
private boolean myHasLinks;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@SequenceGenerator(name="SEQ_RESOURCE_ID", sequenceName="SEQ_RESOURCE_ID")
|
@SequenceGenerator(name = "SEQ_RESOURCE_ID", sequenceName = "SEQ_RESOURCE_ID")
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_RESOURCE_ID")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID")
|
||||||
@Column(name = "RES_ID")
|
@Column(name = "RES_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
|
|
||||||
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
||||||
private Collection<ResourceIndexedSearchParamNumber> myParamsNumber;
|
private Collection<ResourceIndexedSearchParamNumber> myParamsNumber;
|
||||||
|
|
||||||
@Column(name = "SP_NUMBER_PRESENT")
|
@Column(name = "SP_NUMBER_PRESENT")
|
||||||
private boolean myParamsNumberPopulated;
|
private boolean myParamsNumberPopulated;
|
||||||
|
|
||||||
|
@ -540,14 +540,14 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
retVal.setForcedId(getForcedId());
|
retVal.setForcedId(getForcedId());
|
||||||
|
|
||||||
retVal.getTags().clear();
|
retVal.getTags().clear();
|
||||||
|
|
||||||
retVal.setHasTags(isHasTags());
|
retVal.setHasTags(isHasTags());
|
||||||
if (isHasTags()) {
|
if (isHasTags()) {
|
||||||
for (ResourceTag next : getTags()) {
|
for (ResourceTag next : getTags()) {
|
||||||
retVal.addTag(next);
|
retVal.addTag(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hl7.fhir.dstu3.model.Organization;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
|
||||||
|
public class FhirResourceDaoDstu3ExternalReferenceTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() {
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@After
|
||||||
|
public void resetDefaultBehaviour() {
|
||||||
|
// Reset to default
|
||||||
|
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||||
|
myDaoConfig.setTreatBaseUrlsAsLocal(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalReferenceBlockedByDefault() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getManagingOrganization().setReference("Organization/FOO");
|
||||||
|
try {
|
||||||
|
myPatientDao.create(p, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("Resource Organization/FOO not found, specified in path: Patient.managingOrganization", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalReferenceBlockedByDefault() {
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("FOO");
|
||||||
|
org.setName("Org Name");
|
||||||
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
|
||||||
|
try {
|
||||||
|
myPatientDao.create(p, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("Resource contains external reference to URL \"http://example.com/base/Organization/FOO\" but this server is not configured to allow external references", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalReferenceAllowed() {
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("FOO");
|
||||||
|
org.setName("Org Name");
|
||||||
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
|
myDaoConfig.setAllowExternalReferences(true);
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
|
||||||
|
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example.com/base/Organization/FOO"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(pid.getValue()));
|
||||||
|
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example2.com/base/Organization/FOO"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalReferenceReplaced() {
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("FOO");
|
||||||
|
org.setName("Org Name");
|
||||||
|
org.getPartOf().setDisplay("Parent"); // <-- no reference, make sure this works
|
||||||
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
|
Set<String> urls = new HashSet<String>();
|
||||||
|
urls.add("http://example.com/base/");
|
||||||
|
myDaoConfig.setTreatBaseUrlsAsLocal(urls);
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
|
||||||
|
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
p = myPatientDao.read(pid, mySrd);
|
||||||
|
assertEquals("Organization/FOO", p.getManagingOrganization().getReference());
|
||||||
|
|
||||||
|
SearchParameterMap map;
|
||||||
|
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://example.com/base/Organization/FOO"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(pid.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchForInvalidLocalReference() {
|
||||||
|
SearchParameterMap map;
|
||||||
|
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization/FOO"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
|
||||||
|
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization/9999999999"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExternalReferenceReplacedWrongDoesntMatch() {
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("FOO");
|
||||||
|
org.setName("Org Name");
|
||||||
|
org.getPartOf().setDisplay("Parent"); // <-- no reference, make sure this works
|
||||||
|
myOrganizationDao.update(org, mySrd);
|
||||||
|
|
||||||
|
Set<String> urls = new HashSet<String>();
|
||||||
|
urls.add("http://example.com/base/");
|
||||||
|
myDaoConfig.setTreatBaseUrlsAsLocal(urls);
|
||||||
|
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.getManagingOrganization().setReference("http://example.com/base/Organization/FOO");
|
||||||
|
IIdType pid = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
p = myPatientDao.read(pid, mySrd);
|
||||||
|
assertEquals("Organization/FOO", p.getManagingOrganization().getReference());
|
||||||
|
|
||||||
|
SearchParameterMap map;
|
||||||
|
|
||||||
|
// Different base
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("http://foo.com/base/Organization/FOO"));
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -43,6 +43,9 @@ public class TestDstu1Config extends BaseJavaConfigDstu1 {
|
||||||
DaoConfig retVal = new DaoConfig();
|
DaoConfig retVal = new DaoConfig();
|
||||||
retVal.setSubscriptionEnabled(false);
|
retVal.setSubscriptionEnabled(false);
|
||||||
retVal.setAllowMultipleDelete(false);
|
retVal.setAllowMultipleDelete(false);
|
||||||
|
retVal.setAllowExternalReferences(true);
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu1");
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu1");
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
retVal.setAllowMultipleDelete(true);
|
retVal.setAllowMultipleDelete(true);
|
||||||
retVal.setAllowInlineMatchUrlReferences(true);
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
|
retVal.setAllowExternalReferences(true);
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu2");
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu2");
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
retVal.setAllowMultipleDelete(true);
|
retVal.setAllowMultipleDelete(true);
|
||||||
retVal.setAllowInlineMatchUrlReferences(true);
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
|
retVal.setAllowExternalReferences(true);
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu3");
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu3");
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,12 @@
|
||||||
search parameters that did not start with an underscore. E.g. "Patient?_id=1" failed
|
search parameters that did not start with an underscore. E.g. "Patient?_id=1" failed
|
||||||
even though this is a valid conditional reference.
|
even though this is a valid conditional reference.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="363">
|
||||||
|
JPA server can now be configured to allow external references (i.e. references that
|
||||||
|
point to resources on other servers). See
|
||||||
|
<![CDATA[<a href="./doc_jpa.html">JPA Documentation</a>]]> for information on
|
||||||
|
how to use this. Thanks to Naminder Soorma for the suggestion!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5" date="2016-04-20">
|
<release version="1.5" date="2016-04-20">
|
||||||
<action type="fix" issue="339">
|
<action type="fix" issue="339">
|
||||||
|
|
|
@ -147,6 +147,89 @@ $ mvn install]]></source>
|
||||||
Configures the database connection settings
|
Configures the database connection settings
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section name="DaoConfig">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The Spring confguration contains a definition for a bean called <code>daoConfig</code>,
|
||||||
|
which will look something like the following:
|
||||||
|
</p>
|
||||||
|
<pre><![CDATA[@Bean()
|
||||||
|
public DaoConfig daoConfig() {
|
||||||
|
DaoConfig retVal = new DaoConfig();
|
||||||
|
retVal.setAllowMultipleDelete(true);
|
||||||
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
|
return retVal;
|
||||||
|
}]]></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You can use this method to change various configuration settings on the DaoConfig bean
|
||||||
|
which define the way that the JPA server will behave.
|
||||||
|
See the <a href="./apidocs-jpaserver/ca/uhn/fhir/jpa/dao/DaoConfig.html">DaoConfig JavaDoc</a>
|
||||||
|
for information about the available settings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<subsection name="External/Absolute Resource References">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Clients may sometimes post resources to your server that contain
|
||||||
|
absolute resource references. For example, consider the following resource:
|
||||||
|
</p>
|
||||||
|
<pre><![CDATA[<Patient xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="patient-infant-01"/>
|
||||||
|
<name>
|
||||||
|
<use value="official"/>
|
||||||
|
<family value="Miller"/>
|
||||||
|
<given value="Samuel"/>
|
||||||
|
</name>
|
||||||
|
<managingOrganization>
|
||||||
|
<reference value="http://example.com/fhir/Organization/123"/>
|
||||||
|
</managingOrganization>
|
||||||
|
</Patient>]]></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
By default, the server will reject this reference, as only
|
||||||
|
local references are permitted by the server. This can be changed
|
||||||
|
however.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you want the server to recognize that this URL is actually a local
|
||||||
|
reference (i.e. because the server will be deployed to the base URL
|
||||||
|
<code>http://example.com/fhir/</code>) you can
|
||||||
|
configure the server to recognize this URL via the following DaoConfig
|
||||||
|
setting:
|
||||||
|
</p>
|
||||||
|
<pre><![CDATA[@Bean()
|
||||||
|
public DaoConfig daoConfig() {
|
||||||
|
DaoConfig retVal = new DaoConfig();
|
||||||
|
// ... other config ...
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("http://example.com/fhir/");
|
||||||
|
return retVal;
|
||||||
|
}]]></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On the other hand, if you want the server to be configurable to
|
||||||
|
allow remote references, you can set this with the confguration below.
|
||||||
|
Using the <code>setAllowInlineMatchUrlReferences</code> means that
|
||||||
|
it will be possible to search for references that refer to these
|
||||||
|
external references.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre><![CDATA[@Bean()
|
||||||
|
public DaoConfig daoConfig() {
|
||||||
|
DaoConfig retVal = new DaoConfig();
|
||||||
|
// Allow external references
|
||||||
|
retVal.setAllowInlineMatchUrlReferences(true);
|
||||||
|
|
||||||
|
// If you are allowing external references, it is recommended to
|
||||||
|
// also tell the server which references actually will be local
|
||||||
|
retVal.getTreatBaseUrlsAsLocal().add("http://mydomain.com/fhir");
|
||||||
|
return retVal;
|
||||||
|
}]]></pre>
|
||||||
|
</subsection>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section name="Not yet complete">
|
<section name="Not yet complete">
|
||||||
|
|
Loading…
Reference in New Issue