diff --git a/examples/src/main/java/example/AuthorizationInterceptors.java b/examples/src/main/java/example/AuthorizationInterceptors.java index 3f84c3531db..d9ead4acb0a 100644 --- a/examples/src/main/java/example/AuthorizationInterceptors.java +++ b/examples/src/main/java/example/AuthorizationInterceptors.java @@ -1,16 +1,24 @@ package example; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + import java.util.List; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; @@ -87,4 +95,43 @@ public class AuthorizationInterceptors { } //END SNIPPET: patientAndAdmin + + //START SNIPPET: conditionalUpdate + @Update() + public MethodOutcome update( + @IdParam IdDt theId, + @ResourceParam Patient theResource, + @ConditionalUrlParam String theConditionalUrl, + RequestDetails theRequestDetails) { + + // If we're processing a conditional URL... + if (isNotBlank(theConditionalUrl)) { + + // Pretend we've done the conditional processing. Now let's + // notify the interceptors that an update has been performed + // and supply the actual ID that's being updated + IdDt actual = new IdDt("Patient", "1123"); + + // There are a number of possible constructors for ActionRequestDetails. + // You should supply as much detail about the sub-operation as possible + IServerInterceptor.ActionRequestDetails subRequest = + new IServerInterceptor.ActionRequestDetails(theRequestDetails, actual); + + // Notify the interceptors + subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); + } + + // In a real server, perhaps we would process the conditional + // request differently and follow a separate path. Either way, + // let's pretend there is some storage code here. + + theResource.setId(theId.withVersion("2")); + MethodOutcome retVal = new MethodOutcome(); + retVal.setCreated(true); + retVal.setResource(theResource); + return retVal; + } + //END SNIPPET: conditionalUpdate + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeDeclaredChildDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeDeclaredChildDefinition.java index 4009ee6d30d..295e5057660 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeDeclaredChildDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeDeclaredChildDefinition.java @@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBase; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.util.ValidateUtil; public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition { private final IAccessor myAccessor; @@ -46,7 +47,7 @@ public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChil BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException { super(); Validate.notNull(theField, "No field speficied"); - Validate.inclusiveBetween(0, Integer.MAX_VALUE, theChildAnnotation.min(), "Min must be >= 0"); + ValidateUtil.isGreaterThanOrEqualTo(theChildAnnotation.min(), 0, "Min must be >= 0"); Validate.isTrue(theChildAnnotation.max() == -1 || theChildAnnotation.max() >= theChildAnnotation.min(), "Max must be >= Min (unless it is -1 / unlimited)"); Validate.notBlank(theElementName, "Element name must not be blank"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java index 9d5bbd43414..cde6942e331 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java @@ -24,7 +24,6 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.util.CoverageIgnore; public class MethodOutcome { @@ -33,7 +32,6 @@ public class MethodOutcome { private IIdType myId; private IBaseOperationOutcome myOperationOutcome; private IBaseResource myResource; - private IdDt myVersionId; /** * Constructor @@ -91,27 +89,6 @@ public class MethodOutcome { myCreated = theCreated; } - /** - * @deprecated Use the constructor which accepts a single IIdType parameter, and include the logical ID and version ID in that IIdType instance - */ - @Deprecated - @CoverageIgnore - public MethodOutcome(IIdType theId, IdDt theVersionId) { - myId = theId; - myVersionId = theVersionId; - } - - /** - * @deprecated Use the constructor which accepts a single IdDt parameter, and include the logical ID and version ID in that IdDt instance - */ - @Deprecated - @CoverageIgnore - public MethodOutcome(IIdType theId, IdDt theVersionId, IBaseOperationOutcome theBaseOperationOutcome) { - myId = theId; - myVersionId = theVersionId; - myOperationOutcome = theBaseOperationOutcome; - } - /** * Constructor * @@ -122,6 +99,16 @@ public class MethodOutcome { myId = theId; } + /** + * Constructor + * + * @param theOperationOutcome + * The operation outcome resource to return + */ + public MethodOutcome(IBaseOperationOutcome theOperationOutcome) { + myOperationOutcome = theOperationOutcome; + } + /** * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are * returned to client instances, if the server has responded with an HTTP 201 Created. @@ -152,15 +139,6 @@ public class MethodOutcome { return myResource; } - /** - * @deprecated {@link MethodOutcome#getId()} should return the complete ID including version if it is available - */ - @Deprecated - @CoverageIgnore - public IdDt getVersionId() { - return myVersionId; - } - /** * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called whether the * result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist. @@ -205,13 +183,4 @@ public class MethodOutcome { myResource = theResource; } - /** - * @deprecated Put the ID and version ID into the same IdDt instance and pass it to {@link #setId(IIdType)} - */ - @Deprecated - @CoverageIgnore - public void setVersionId(IdDt theVersionId) { - myVersionId = theVersionId; - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 7d246881f68..8d3e964ca30 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -1663,7 +1663,7 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws BaseServerResponseException { - MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); + MethodOutcome response = MethodUtil.process2xxResponse(myContext, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { response.setCreated(true); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index 59a22ff5e09..bcd7c825b7e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -40,7 +40,6 @@ import org.hl7.fhir.instance.model.api.IIdType; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; @@ -167,7 +166,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding -1) { - int idIndexStart = resourceIndex + resourceNamePart.length(); - int idIndexEnd = theLocationHeader.indexOf('/', idIndexStart); - if (idIndexEnd == -1) { - // nothing - } else { - String versionIdPart = "/_history/"; - int historyIdStart = theLocationHeader.indexOf(versionIdPart, idIndexEnd); - if (historyIdStart != -1) { - theOutcomeToPopulate.setVersionId(new IdDt(theLocationHeader.substring(historyIdStart + versionIdPart.length()))); - } - } - } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index 384f92d477c..34445b4d835 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -747,7 +747,7 @@ public class MethodUtil { } - public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map> theHeaders) { + public static MethodOutcome process2xxResponse(FhirContext theContext, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map> theHeaders) { List locationHeaders = new ArrayList(); List lh = theHeaders.get(Constants.HEADER_LOCATION_LC); if (lh != null) { @@ -761,7 +761,7 @@ public class MethodUtil { MethodOutcome retVal = new MethodOutcome(); if (locationHeaders != null && locationHeaders.size() > 0) { String locationHeader = locationHeaders.get(0); - BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, theResourceName, locationHeader); + BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader); } if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) { EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java index 81151e40c9d..534723a01a0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java @@ -112,7 +112,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { myName = theOperationName; if (theContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU1)) { - throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name()); + throw new ConfigurationException("@" + Operation.class.getSimpleName() + " methods are not supported on servers for FHIR version " + theContext.getVersion().getVersion().name() + " - Found one on class " + theMethod.getDeclaringClass().getName()); } if (theReturnTypeFromRp != null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java index 77bca6ce840..acef735e3e6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/UpdateMethodBinding.java @@ -83,7 +83,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe if (isNotBlank(locationHeader)) { MethodOutcome mo = new MethodOutcome(); - parseContentLocation(getContext(), mo, getResourceName(), locationHeader); + parseContentLocation(getContext(), mo, locationHeader); if (mo.getId() == null || mo.getId().isEmpty()) { throw new InvalidRequestException("Invalid Content-Location header for resource " + getResourceName() + ": " + locationHeader); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java index fab42ef1793..5b8fc4a3f3e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java @@ -26,7 +26,6 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -41,6 +40,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.method.QualifiedParamList; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.ValidateUtil; @SuppressWarnings("deprecation") public class DateParam extends BaseParamWithPrefix implements IQueryParameterType , IQueryParameterOr { @@ -81,7 +81,7 @@ public class DateParam extends BaseParamWithPrefix implements IQueryP * Constructor */ public DateParam(ParamPrefixEnum thePrefix, long theDate) { - Validate.inclusiveBetween(1, Long.MAX_VALUE, theDate, "theDate must not be 0 or negative"); + ValidateUtil.isGreaterThan(theDate, 0, "theDate must not be 0 or negative"); setPrefix(thePrefix); setValue(new Date(theDate)); } @@ -138,7 +138,7 @@ public class DateParam extends BaseParamWithPrefix implements IQueryP */ @Deprecated public DateParam(QuantityCompararatorEnum theComparator, long theDate) { - Validate.inclusiveBetween(1, Long.MAX_VALUE, theDate, "theDate must not be 0 or negative"); + ValidateUtil.isGreaterThan(theDate, 0, "theDate must not be 0 or negative"); setPrefix(toPrefix(theComparator)); setValue(new Date(theDate)); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index a6a9319b3cb..ea41b928831 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -176,9 +176,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer implements IDao { } @SuppressWarnings("unchecked") - protected ResourceTable updateEntity(final IBaseResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, RequestDetails theRequestDetails) { + protected ResourceTable updateEntity(final IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime) { ourLog.info("Starting entity update"); /* @@ -1085,7 +1084,7 @@ public abstract class BaseHapiFhirDao implements IDao { */ if (theResource != null) { if (thePerformIndexing) { - validateResourceForStorage((T) theResource, theEntity, theRequestDetails); + validateResourceForStorage((T) theResource, theEntity); } String resourceType = myContext.getResourceDefinition(theResource).getName(); if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) { @@ -1175,7 +1174,7 @@ public abstract class BaseHapiFhirDao implements IDao { coordsParams = extractSearchParamCoords(theEntity, theResource); // ourLog.info("Indexing resource: {}", entity.getId()); - ourLog.info("Storing date indexes: {}", dateParams); + ourLog.trace("Storing date indexes: {}", dateParams); tokenParams = new HashSet(); for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) { @@ -1394,9 +1393,8 @@ public abstract class BaseHapiFhirDao implements IDao { return theEntity; } - protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, Date theUpdateTime, - RequestDetails theRequestDetails) { - return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true, theUpdateTime, theRequestDetails); + protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) { + return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime); } protected void validateDeleteConflictsEmptyOrThrowException(List theDeleteConflicts) { @@ -1423,10 +1421,8 @@ public abstract class BaseHapiFhirDao implements IDao { * The resource that is about to be persisted * @param theEntityToSave * TODO - * @param theRequestDetails - * TODO */ - protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) { + protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) { Object tag = null; if (theResource instanceof IResource) { IResource res = (IResource) theResource; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 648f8285719..dc1a608192e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -197,20 +197,26 @@ public abstract class BaseHapiFhirResourceDao extends B validateOkToDelete(deleteConflicts, entity); // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType(), getContext(), theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); - + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId); + notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); + } + Date updateTime = new Date(); - ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime, theRequestDetails); + ResourceTable savedEntity = updateEntity(null, entity, updateTime, updateTime); // Notify JPA interceptors - T resourceToDelete = toResource(myResourceType, entity, false); - theRequestDetails.getRequestOperationCallback().resourceDeleted(resourceToDelete); - for (IServerInterceptor next : getConfig().getInterceptors()) { - if (next instanceof IJpaServerInterceptor) { - ((IJpaServerInterceptor) next).resourceDeleted(requestDetails, entity); + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId); + T resourceToDelete = toResource(myResourceType, entity, false); + theRequestDetails.getRequestOperationCallback().resourceDeleted(resourceToDelete); + for (IServerInterceptor next : getConfig().getInterceptors()) { + if (next instanceof IJpaServerInterceptor) { + ((IJpaServerInterceptor) next).resourceDeleted(requestDetails, entity); + } } } + return savedEntity; } @@ -245,12 +251,12 @@ public abstract class BaseHapiFhirResourceDao extends B // Notify interceptors IdDt idToDelete = entity.getIdDt(); - ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, idToDelete.getResourceType(), idToDelete); notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); // Perform delete Date updateTime = new Date(); - updateEntity(null, entity, true, updateTime, updateTime, theRequestDetails); + updateEntity(null, entity, updateTime, updateTime); // Notify JPA interceptors T resourceToDelete = toResource(myResourceType, entity, false); @@ -322,21 +328,26 @@ public abstract class BaseHapiFhirResourceDao extends B } // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getIdElement(), toResourceName(theResource), theResource, getContext(), theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails); - + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theResource); + notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails); + } + // Perform actual DB update - updateEntity(theResource, entity, false, null, thePerformIndexing, true, theUpdateTime, theRequestDetails); + updateEntity(theResource, entity, null, thePerformIndexing, true, theUpdateTime); theResource.setId(entity.getIdDt()); // Notify JPA interceptors - theRequestDetails.getRequestOperationCallback().resourceCreated(theResource); - for (IServerInterceptor next : getConfig().getInterceptors()) { - if (next instanceof IJpaServerInterceptor) { - ((IJpaServerInterceptor) next).resourceCreated(requestDetails, entity); + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theResource); + theRequestDetails.getRequestOperationCallback().resourceCreated(theResource); + for (IServerInterceptor next : getConfig().getInterceptors()) { + if (next instanceof IJpaServerInterceptor) { + ((IJpaServerInterceptor) next).resourceCreated(requestDetails, entity); + } } } - + DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true); String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart()); @@ -401,7 +412,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public TagList getAllResourceTags(RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails); StopWatch w = new StopWatch(); @@ -424,7 +435,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public TagList getTags(IIdType theResourceId, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, null, theResourceId); notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails); StopWatch w = new StopWatch(); @@ -436,7 +447,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails); StopWatch w = new StopWatch(); @@ -448,7 +459,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public IBundleProvider history(final IIdType theId, final Date theSince, Date theUntil, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails); StopWatch w = new StopWatch(); @@ -547,7 +558,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId); notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails); StopWatch w = new StopWatch(); @@ -577,7 +588,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId); notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails); StopWatch w = new StopWatch(); @@ -609,7 +620,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public MT metaGetOperation(Class theType, IIdType theId, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); notifyInterceptors(RestOperationTypeEnum.META, requestDetails); Set tagDefs = new HashSet(); @@ -628,7 +639,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public MT metaGetOperation(Class theType, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.META, requestDetails); String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)"; @@ -704,10 +715,12 @@ public abstract class BaseHapiFhirResourceDao extends B validateResourceTypeAndThrowIllegalArgumentException(theId); // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails); - RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ; - notifyInterceptors(operationType, requestDetails); - + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); + RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ; + notifyInterceptors(operationType, requestDetails); + } + StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theId); validateResourceType(entity); @@ -790,14 +803,14 @@ public abstract class BaseHapiFhirResourceDao extends B } @Override - public void reindex(T theResource, ResourceTable theEntity, RequestDetails theRequestDetails) { - updateEntity(theResource, theEntity, false, null, true, false, theEntity.getUpdatedDate(), theRequestDetails); + public void reindex(T theResource, ResourceTable theEntity) { + updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate()); } @Override public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails); StopWatch w = new StopWatch(); @@ -838,7 +851,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public IBundleProvider search(final SearchParameterMap theParams) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theParams.getRequestDetails()); + ActionRequestDetails requestDetails = new ActionRequestDetails(theParams.getRequestDetails(), getContext(), getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails); SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, @@ -1010,11 +1023,11 @@ public abstract class BaseHapiFhirResourceDao extends B } // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(resourceId, getResourceName(), theResource, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theResource, getResourceName(), resourceId); notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails); // Perform update - ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true, new Date(), theRequestDetails); + ResourceTable savedEntity = updateEntity(theResource, entity, null, thePerformIndexing, true, new Date()); // Notify interceptors theRequestDetails.getRequestOperationCallback().resourceUpdated(theResource); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index eecc2d43ba6..5b5f46976b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -37,8 +37,6 @@ import javax.persistence.criteria.Root; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Propagation; @@ -50,7 +48,6 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.entity.ForcedId; import ca.uhn.fhir.jpa.entity.ResourceTable; -import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.util.ReindexFailureException; import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.model.api.TagList; @@ -79,21 +76,20 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao() { @SuppressWarnings("unchecked") @Override @@ -135,7 +131,7 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao extends BaseHapiFhirDao() { - @Override - public Integer doInTransaction(TransactionStatus theStatus) { - - int maxResult = 10000; - Page resources = myTermConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult)); - if (resources.hasContent() == false) { - return 0; - } - - ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements()); - - int count = 0; - long start = System.currentTimeMillis(); - - for (TermConcept resourceTable : resources) { - resourceTable.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); - myTermConceptDao.save(resourceTable); - count++; - } - - long delay = System.currentTimeMillis() - start; - long avg = (delay / resources.getContent().size()); - ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), delay, avg }); - - return resources.getContent().size(); - } - }); - } - @Override public TagList getAllTags(RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails); StopWatch w = new StopWatch(); @@ -221,7 +186,7 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao extends BaseHapiFhirDao extends BaseHapiFhirResou @Override public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequestDetails) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theResource, null, theId); notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails); if (theMode == ValidationModeEnum.DELETE) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java index 958c3e4210d..793aa6aa17c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java @@ -73,7 +73,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2im @Override public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails); return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative); @@ -82,7 +82,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2im @Override public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails); return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java index 2685aacb398..0af33df83f4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoQuestionnaireResponseDstu2.java @@ -35,7 +35,6 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Questionnaire; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; import ca.uhn.fhir.model.dstu2.resource.ValueSet; -import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.validation.FhirValidator; @@ -64,8 +63,8 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs } @Override - protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) { - super.validateResourceForStorage(theResource, theEntityToSave, theRequestDetails); + protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave) { + super.validateResourceForStorage(theResource, theEntityToSave); if (!myValidateResponses) { return; } @@ -80,7 +79,7 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs val.setValidateAgainstStandardSchematron(false); FhirQuestionnaireResponseValidator module = new FhirQuestionnaireResponseValidator(); - module.setResourceLoader(new JpaResourceLoader(theRequestDetails)); + module.setResourceLoader(new JpaResourceLoader()); val.registerValidatorModule(module); ValidationResult result = val.validateWithResult(myRefImplCtx.newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(qa))); @@ -92,10 +91,8 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs public class JpaResourceLoader implements IResourceLoader { - private RequestDetails myRequestDetails; - - public JpaResourceLoader(RequestDetails theRequestDetails) { - myRequestDetails = theRequestDetails; + public JpaResourceLoader() { + super(); } @Override @@ -107,7 +104,7 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs */ if ("ValueSet".equals(theType.getSimpleName())) { IFhirResourceDao dao = getDao(ValueSet.class); - ValueSet in = dao.read(theId, myRequestDetails); + ValueSet in = dao.read(theId, null); String encoded = getContext().newJsonParser().encodeResourceToString(in); // TODO: this is temporary until structures-dstu2 catches up to structures-hl7org.dstu2 @@ -116,7 +113,7 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs return myRefImplCtx.newJsonParser().parseResource(theType, encoded); } else if ("Questionnaire".equals(theType.getSimpleName())) { IFhirResourceDao dao = getDao(Questionnaire.class); - Questionnaire vs = dao.read(theId, myRequestDetails); + Questionnaire vs = dao.read(theId, null); return myRefImplCtx.newJsonParser().parseResource(theType, getContext().newJsonParser().encodeResourceToString(vs)); } else { // Should not happen, validator will only ask for these two diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java index 13fa6cfc488..b2bf9bd1bfd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java @@ -27,8 +27,6 @@ import org.springframework.scheduling.annotation.Scheduled; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.SearchParameter; -import ca.uhn.fhir.rest.method.RequestDetails; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2implements IFhirResourceDaoSearchParameter { @@ -48,10 +46,9 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2 dao = getDao(resourceDef.getImplementingClass()); @@ -252,7 +251,7 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2() { @Override public Void doInTransaction(TransactionStatus theStatus) { - delete(subscriptionId, new ServletRequestDetails()); + delete(subscriptionId, null); return null; } }); @@ -260,9 +259,9 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2 } @Override - public ValueSet expand(IIdType theId, String theFilter) { + public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) { ValueSet source = loadValueSetForExpansion(theId); return expand(source, theFilter); @@ -179,7 +179,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 } @Override - public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept) { + public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { List valueSetIds; boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0; @@ -212,7 +212,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 } for (IIdType nextId : valueSetIds) { - ValueSet expansion = expand(nextId, null); + ValueSet expansion = expand(nextId, null, theRequestDetails); List contains = expansion.getExpansion().getContains(); ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); if (result != null) { @@ -314,7 +314,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 List valueSetIds = findCodeSystemIdsContainingSystemAndCode(code, system); for (IIdType nextId : valueSetIds) { - ValueSet expansion = expand(nextId, null); + ValueSet expansion = expand(nextId, null, theRequestDetails); List contains = expansion.getExpansion().getContains(); LookupCodeResult result = lookup(contains, system, code); if (result != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java index ce5bdb9ebe3..86d6c88eaea 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1.java @@ -64,7 +64,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao, M ourLog.info("Beginning transaction with {} resources", theResources.size()); // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); long start = System.currentTimeMillis(); @@ -270,7 +270,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao, M ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull)); } - updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull, updateTime, theRequestDetails); + updateEntity(resource, table, deletedTimestampOrNull, updateTime); } long delay = System.currentTimeMillis() - start; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index c3bb1fa05c9..3e56c194241 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -200,7 +200,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { @Override public MetaDt metaGetOperation(RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.META, requestDetails); String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; @@ -259,7 +259,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { @Transactional(propagation = Propagation.REQUIRED) @Override public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) { - ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null); notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); String actionName = "Transaction"; @@ -474,7 +474,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; - updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false, updateTime, theRequestDetails); + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime); } myEntityManager.flush(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java index 1be0d79d477..dd44dfc8818 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java @@ -143,9 +143,8 @@ public interface IFhirResourceDao extends IDao { /** * Updates index tables associated with the given resource. Does not create a new * version or update the resource's update time. - * @param theRequestDetails TODO */ - void reindex(T theResource, ResourceTable theEntity, RequestDetails theRequestDetails); + void reindex(T theResource, ResourceTable theEntity); void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoValueSet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoValueSet.java index ade8b580700..3e1cd0e499e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoValueSet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoValueSet.java @@ -24,9 +24,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import ca.uhn.fhir.rest.method.RequestDetails; + public interface IFhirResourceDaoValueSet extends IFhirResourceDao { - T expand(IIdType theId, String theFilter); + T expand(IIdType theId, String theFilter, RequestDetails theRequestDetails); T expand(T theSource, String theFilter); @@ -34,7 +36,7 @@ public interface IFhirResourceDaoValueSet exten void purgeCaches(); - ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept); + ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); public class ValidateCodeResult { private String myDisplay; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java index cbb4831e0c6..b5f6ea52be6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java @@ -62,7 +62,7 @@ public interface IFhirSystemDao extends IDao { */ MT metaGetOperation(RequestDetails theRequestDetails); - int performReindexingPass(Integer theCount, RequestDetails theRequestDetails); + int performReindexingPass(Integer theCount); T transaction(RequestDetails theRequestDetails, T theResources); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 53d8144b41d..5681974830f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -35,7 +35,6 @@ import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -198,9 +197,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 extends BaseHapiFhirRe @Override public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequestDetails) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null, theResource, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theResource, null, theId); notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails); if (theMode == ValidationModeEnum.DELETE) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java index b7160e8e9cf..6dfeb25f0ad 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java @@ -72,7 +72,7 @@ public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3im @Override public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails); return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative); @@ -81,7 +81,7 @@ public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3im @Override public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails); return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java index b7665883a88..e1f14d6da19 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java @@ -29,8 +29,6 @@ import org.springframework.scheduling.annotation.Scheduled; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; -import ca.uhn.fhir.rest.method.RequestDetails; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3implements IFhirResourceDaoSearchParameter { @@ -50,10 +48,9 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3() { @Override public Void doInTransaction(TransactionStatus theStatus) { - delete(subscriptionId, new ServletRequestDetails()); + delete(subscriptionId, null); return null; } }); @@ -265,9 +264,9 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3 private IFhirResourceDaoCodeSystem myCodeSystemDao; @Override - public ValueSet expand(IIdType theId, String theFilter) { - ValueSet source = myValidationSupport.fetchResource(getContext(), ValueSet.class, theId.getValue()); + public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) { + ValueSet source = read(theId, theRequestDetails); return expand(source, theFilter); } @@ -87,6 +89,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion(); ValueSet retVal = new ValueSet(); + retVal.getMeta().setLastUpdated(new Date()); retVal.setExpansion(expansion); return retVal; } @@ -167,7 +170,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 @Override public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, - IPrimitiveType theSystem, IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept) { + IPrimitiveType theSystem, IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { List valueSetIds = Collections.emptyList(); @@ -225,7 +228,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 // } for (IIdType nextId : valueSetIds) { - ValueSet expansion = expand(nextId, null); + ValueSet expansion = expand(nextId, null, theRequestDetails); List contains = expansion.getExpansion().getContains(); ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); if (result != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index e5147574dd3..86d34206fe6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -205,7 +205,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { @Override public Meta metaGetOperation(RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); notifyInterceptors(RestOperationTypeEnum.META, requestDetails); String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; @@ -264,7 +264,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { @Transactional(propagation = Propagation.REQUIRED) @Override public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) { - ActionRequestDetails requestDetails = new ActionRequestDetails(null, "Bundle", theRequest, getContext(), theRequestDetails); + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null); notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); String actionName = "Transaction"; @@ -488,7 +488,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; - updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false, updateTime, theRequestDetails); + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime); } myEntityManager.flush(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index db13d0ee3df..23c31396a28 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -39,6 +40,8 @@ import javax.persistence.Index; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.PrePersist; +import javax.persistence.PreUpdate; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Transient; @@ -49,47 +52,56 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory; import org.hibernate.search.annotations.Analyze; import org.hibernate.search.annotations.Analyzer; +import org.hibernate.search.annotations.AnalyzerDef; +import org.hibernate.search.annotations.AnalyzerDefs; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Fields; import org.hibernate.search.annotations.Indexed; import org.hibernate.search.annotations.Store; +import org.hibernate.search.annotations.TokenizerDef; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor; //@formatter:off @Entity -@Indexed() +@Indexed(interceptor=DeferConceptIndexingInterceptor.class) @Table(name="TRM_CONCEPT", uniqueConstraints= { @UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"}) }, indexes= { @Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList="INDEX_STATUS") }) +@AnalyzerDefs({ + @AnalyzerDef(name = "conceptParentPidsAnalyzer", + tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), + filters = { + }) +}) //@formatter:on public class TermConcept implements Serializable { private static final int MAX_DESC_LENGTH = 400; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermConcept.class); + private static final long serialVersionUID = 1L; - @OneToMany(fetch=FetchType.LAZY, mappedBy="myParent") + @OneToMany(fetch = FetchType.LAZY, mappedBy = "myParent", cascade= {}) private Collection myChildren; - - @Column(name="CODE", length=100, nullable=false) - @Fields({ - @Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")), - }) + + @Column(name = "CODE", length = 100, nullable = false) + @Fields({ @Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")), }) private String myCode; @ManyToOne() - @JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID")) + @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID")) private TermCodeSystemVersion myCodeSystem; - @Column(name="CODESYSTEM_PID", insertable=false, updatable=false) - @Fields({ - @Field(name="myCodeSystemVersionPid") - }) + @Column(name = "CODESYSTEM_PID", insertable = false, updatable = false) + @Fields({ @Field(name = "myCodeSystemVersionPid") }) private long myCodeSystemVersionPid; - + //@formatter:off @Column(name="DISPLAY", length=MAX_DESC_LENGTH, nullable=true) @Fields({ @@ -100,23 +112,21 @@ public class TermConcept implements Serializable { }) private String myDisplay; //@formatter:on - + @Id() - @SequenceGenerator(name="SEQ_CONCEPT_PID", sequenceName="SEQ_CONCEPT_PID") - @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PID") - @Column(name="PID") + @SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PID") + @Column(name = "PID") private Long myId; @Column(name = "INDEX_STATUS", nullable = true) private Long myIndexStatus; @Transient - @Fields({ - @Field(name = "myParentPids", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")), - }) + @Field(name = "myParentPids", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "conceptParentPidsAnalyzer")) private String myParentPids; - @OneToMany(cascade= {}, fetch=FetchType.LAZY, mappedBy="myChild") + @OneToMany(cascade = {}, fetch = FetchType.LAZY, mappedBy = "myChild") private Collection myParents; public TermConcept() { @@ -135,7 +145,7 @@ public class TermConcept implements Serializable { link.setChild(theChild); link.setRelationshipType(theRelationshipType); getChildren().add(link); - + theChild.getParents().add(link); return this; } @@ -154,8 +164,8 @@ public class TermConcept implements Serializable { if (theObj == this) { return true; } - - TermConcept obj = (TermConcept)theObj; + + TermConcept obj = (TermConcept) theObj; EqualsBuilder b = new EqualsBuilder(); b.append(myCodeSystem, obj.myCodeSystem); @@ -190,13 +200,17 @@ public class TermConcept implements Serializable { return myIndexStatus; } + public String getParentPidsAsString() { + return myParentPids; + } + public Collection getParents() { if (myParents == null) { myParents = new ArrayList(); } return myParents; } - + @Override public int hashCode() { HashCodeBuilder b = new HashCodeBuilder(); @@ -204,7 +218,29 @@ public class TermConcept implements Serializable { b.append(myCode); return b.toHashCode(); } - + + private void parentPids(TermConcept theNextConcept, Set theParentPids) { + for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()) { + TermConcept parent = nextParentLink.getParent(); + Long parentConceptId = parent.getId(); + Validate.notNull(parentConceptId); + if (parent != null && theParentPids.add(parentConceptId)) { + parentPids(parent, theParentPids); + } + } + } + + @PreUpdate + @PrePersist + public void prePersist() { + Set parentPids = new HashSet(); + TermConcept entity = this; + parentPids(entity, parentPids); + entity.setParentPids(parentPids); + + ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString()); + } + public void setCode(String theCode) { myCode = theCode; } @@ -236,7 +272,11 @@ public class TermConcept implements Serializable { } b.append(next); } - + + if (b.length() == 0) { + b.append("NONE"); + } + myParentPids = b.toString(); } @@ -244,5 +284,4 @@ public class TermConcept implements Serializable { public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build(); } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java index 36c15af5e9c..7bf7355d3c6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java @@ -96,7 +96,55 @@ public class TermConceptParentChildLink implements Serializable { } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((myChild == null) ? 0 : myChild.hashCode()); + result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode()); + result = prime * result + ((myParent == null) ? 0 : myParent.hashCode()); + result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TermConceptParentChildLink other = (TermConceptParentChildLink) obj; + if (myChild == null) { + if (other.myChild != null) + return false; + } else if (!myChild.equals(other.myChild)) + return false; + if (myCodeSystem == null) { + if (other.myCodeSystem != null) + return false; + } else if (!myCodeSystem.equals(other.myCodeSystem)) + return false; + if (myParent == null) { + if (other.myParent != null) + return false; + } else if (!myParent.equals(other.myParent)) + return false; + if (myRelationshipType != other.myRelationshipType) + return false; + return true; + } + + public enum RelationshipTypeEnum{ + // ******************************************** + // IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED ISA } + + + public Long getId() { + return myPid; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java index 38a87c41aad..c34e07cc683 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java @@ -53,7 +53,8 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst @IdParam(optional=true) IdDt theId, @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, @OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier, - @OperationParam(name = "filter", min=0, max=1) StringDt theFilter) { + @OperationParam(name = "filter", min=0, max=1) StringDt theFilter, + RequestDetails theRequestDetails) { //@formatter:on boolean haveId = theId != null && theId.hasIdPart(); @@ -72,7 +73,7 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); if (haveId) { - return dao.expand(theId, toFilterString(theFilter)); + return dao.expand(theId, toFilterString(theFilter), theRequestDetails); } else if (haveIdentifier) { return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); } else { @@ -155,14 +156,15 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst @OperationParam(name="system", min=0, max=1) UriDt theSystem, @OperationParam(name="display", min=0, max=1) StringDt theDisplay, @OperationParam(name="coding", min=0, max=1) CodingDt theCoding, - @OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept + @OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept, + RequestDetails theRequestDetails ) { //@formatter:on startRequest(theServletRequest); try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept); + ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails); Parameters retVal = new Parameters(); retVal.addParameter().setName("result").setValue(new BooleanDt(result.isResult())); if (isNotBlank(result.getMessage())) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java index 6420142436a..3abe39bae35 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java @@ -24,6 +24,9 @@ import java.util.Date; import javax.servlet.http.HttpServletRequest; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Required; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; @@ -31,22 +34,36 @@ import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.rest.annotation.At; import ca.uhn.fhir.rest.annotation.GetTags; import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.IBundleProvider; +import ca.uhn.fhir.util.ParametersUtil; public class BaseJpaSystemProvider extends BaseJpaProvider { + public static final String MARK_ALL_RESOURCES_FOR_REINDEXING = "$mark-all-resources-for-reindexing"; + private IFhirSystemDao myDao; public BaseJpaSystemProvider() { // nothing } - @Required - public void setDao(IFhirSystemDao theDao) { - myDao = theDao; + @GetTags + public TagList getAllTagsOnServer(HttpServletRequest theRequest, RequestDetails theRequestDetails) { + startRequest(theRequest); + try { + return myDao.getAllTags(theRequestDetails); + } finally { + endRequest(theRequest); + } + } + + protected IFhirSystemDao getDao() { + return myDao; } @History @@ -60,18 +77,9 @@ public class BaseJpaSystemProvider extends BaseJpaProvider { } } - protected IFhirSystemDao getDao() { - return myDao; - } - - @GetTags - public TagList getAllTagsOnServer(HttpServletRequest theRequest, RequestDetails theRequestDetails) { - startRequest(theRequest); - try { - return myDao.getAllTags(theRequestDetails); - } finally { - endRequest(theRequest); - } + @Required + public void setDao(IFhirSystemDao theDao) { + myDao = theDao; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java new file mode 100644 index 00000000000..cadce02eaba --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProviderDstu2Plus.java @@ -0,0 +1,30 @@ +package ca.uhn.fhir.jpa.provider; + +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.util.ParametersUtil; + +public abstract class BaseJpaSystemProviderDstu2Plus extends BaseJpaSystemProvider { + + //@formatter:off + @Operation(name=MARK_ALL_RESOURCES_FOR_REINDEXING, idempotent=true, returnParameters= { + @OperationParam(name="status") + }) + //@formatter:on + public IBaseResource markAllResourcesForReindexing() { + int count = getDao().markAllResourcesForReindexing(); + + IBaseParameters retVal = ParametersUtil.newInstance(getContext()); + + IPrimitiveType string = ParametersUtil.createString(getContext(), "Marked " + count + " resources"); + ParametersUtil.addParameterToParameters(getContext(), retVal, string, "status"); + + return retVal; + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java index 9b06c270cd8..748999c21bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaSystemProviderDstu2.java @@ -50,7 +50,7 @@ import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider { +public class JpaSystemProviderDstu2 extends BaseJpaSystemProviderDstu2Plus { @Autowired() @Qualifier("mySystemDaoDstu2") @@ -169,18 +169,6 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider { @@ -50,7 +51,8 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst @IdParam(optional=true) IdType theId, @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, @OperationParam(name="identifier", min=0, max=1) UriType theIdentifier, - @OperationParam(name = "filter", min=0, max=1) StringType theFilter) { + @OperationParam(name = "filter", min=0, max=1) StringType theFilter, + RequestDetails theRequestDetails) { //@formatter:on boolean haveId = theId != null && theId.hasIdPart(); @@ -69,7 +71,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); if (haveId) { - return dao.expand(theId, toFilterString(theFilter)); + return dao.expand(theId, toFilterString(theFilter), theRequestDetails); } else if (haveIdentifier) { return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); } else { @@ -102,14 +104,15 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst @OperationParam(name="system", min=0, max=1) UriType theSystem, @OperationParam(name="display", min=0, max=1) StringType theDisplay, @OperationParam(name="coding", min=0, max=1) Coding theCoding, - @OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept + @OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept, + RequestDetails theRequestDetails ) { //@formatter:on startRequest(theServletRequest); try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept); + ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails); Parameters retVal = new Parameters(); retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult())); if (isNotBlank(result.getMessage())) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java index c242f24c482..0ef33b033d6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaSystemProviderDstu3.java @@ -41,7 +41,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider; +import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -51,7 +51,7 @@ import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider { +public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus { @Autowired() @Qualifier("mySystemDaoDstu3") @@ -170,19 +170,6 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider return retVal; } - //@formatter:off - @Operation(name="$mark-all-resources-for-reindexing", idempotent=true, returnParameters= { - @OperationParam(name="count", type=IntegerType.class) - }) - //@formatter:on - public Parameters markAllResourcesForReindexing() { - int count = mySystemDao.markAllResourcesForReindexing(); - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("count").setValue(new IntegerType(count)); - return retVal; - } - //@formatter:off @Operation(name="$meta", idempotent=true, returnParameters= { @OperationParam(name="return", type=Meta.class) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java index 06162186e2d..62d20c9420d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java @@ -48,13 +48,15 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider { + public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderDstu3.class); @Autowired private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; //@formatter:off - @Operation(name = "$upload-external-code-system", idempotent = false, returnParameters= { + @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters= { @OperationParam(name="conceptCount", type=IntegerType.class, min=1) }) public Parameters uploadExternalCodeSystem( diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java new file mode 100644 index 00000000000..405016638bf --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DeferConceptIndexingInterceptor.java @@ -0,0 +1,35 @@ +package ca.uhn.fhir.jpa.search; + +import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor; +import org.hibernate.search.indexes.interceptor.IndexingOverride; + +import ca.uhn.fhir.jpa.entity.TermConcept; + +public class DeferConceptIndexingInterceptor implements EntityIndexingInterceptor { + + @Override + public IndexingOverride onAdd(TermConcept theEntity) { + if (theEntity.getIndexStatus() == null) { + return IndexingOverride.SKIP; + } + + return IndexingOverride.APPLY_DEFAULT; + } + + @Override + public IndexingOverride onCollectionUpdate(TermConcept theEntity) { + return IndexingOverride.APPLY_DEFAULT; + } + + + @Override + public IndexingOverride onDelete(TermConcept theEntity) { + return IndexingOverride.APPLY_DEFAULT; + } + + @Override + public IndexingOverride onUpdate(TermConcept theEntity) { + return onAdd(theEntity); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu2.java index adadb621aa2..5acbf713d8d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu2.java @@ -146,7 +146,7 @@ public class SubscriptionWebsocketHandlerDstu2 extends TextWebSocketHandler impl public void closing() { ourLog.info("Deleting subscription {}", mySubscriptionId); try { - mySubscriptionDao.delete(mySubscriptionId, new ServletRequestDetails()); + mySubscriptionDao.delete(mySubscriptionId, null); } catch (Exception e) { handleFailure(e); } @@ -233,7 +233,7 @@ public class SubscriptionWebsocketHandlerDstu2 extends TextWebSocketHandler impl } try { - Subscription subscription = mySubscriptionDao.read(id, new ServletRequestDetails()); + Subscription subscription = mySubscriptionDao.read(id, null); mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); myState = new BoundStaticSubscipriptionState(theSession); @@ -270,7 +270,7 @@ public class SubscriptionWebsocketHandlerDstu2 extends TextWebSocketHandler impl } } - IIdType id = mySubscriptionDao.create(subscription, new ServletRequestDetails()).getId(); + IIdType id = mySubscriptionDao.create(subscription, null).getId(); mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu3.java index d5c8c54bb71..459a1736f0a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionWebsocketHandlerDstu3.java @@ -146,7 +146,7 @@ public class SubscriptionWebsocketHandlerDstu3 extends TextWebSocketHandler impl public void closing() { ourLog.info("Deleting subscription {}", mySubscriptionId); try { - mySubscriptionDao.delete(mySubscriptionId, new ServletRequestDetails()); + mySubscriptionDao.delete(mySubscriptionId, null); } catch (Exception e) { handleFailure(e); } @@ -233,7 +233,7 @@ public class SubscriptionWebsocketHandlerDstu3 extends TextWebSocketHandler impl } try { - Subscription subscription = mySubscriptionDao.read(id, new ServletRequestDetails()); + Subscription subscription = mySubscriptionDao.read(id, null); mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); myState = new BoundStaticSubscipriptionState(theSession); @@ -270,7 +270,7 @@ public class SubscriptionWebsocketHandlerDstu3 extends TextWebSocketHandler impl } } - IIdType id = mySubscriptionDao.create(subscription, new ServletRequestDetails()).getId(); + IIdType id = mySubscriptionDao.create(subscription, null).getId(); mySubscriptionPid = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(id); mySubscriptionId = subscription.getIdElement(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java index 0f7391e8561..843b80aea16 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvc.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term; */ import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.IdentityHashMap; @@ -32,10 +33,17 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; +import org.apache.commons.lang3.time.DateUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; import com.google.common.base.Stopwatch; @@ -50,6 +58,8 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ObjectUtil; @@ -85,6 +95,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { protected EntityManager myEntityManager; private boolean myProcessDeferred = true; + private long myNextReindexPass; private boolean addToSet(Set theSetToPopulate, TermConcept theConcept) { boolean retVal = theSetToPopulate.add(theConcept); @@ -209,15 +220,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { return cs; } - private void parentPids(TermConcept theNextConcept, Set theParentPids) { - for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){ - TermConcept parent = nextParentLink.getParent(); - if (parent != null && theParentPids.add(parent.getId())) { - parentPids(parent, theParentPids); - } - } - } - private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap theConceptsStack, int theTotalConcepts) { if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { return; @@ -231,12 +233,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { theConcept.setCodeSystem(theCodeSystem); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); - Set parentPids = new HashSet(); - parentPids(theConcept, parentPids); - theConcept.setParentPids(parentPids); - if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { - myConceptDao.save(theConcept); + saveConcept(theConcept); } else { myConceptsToSaveLater.add(theConcept); } @@ -247,7 +245,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { for (TermConceptParentChildLink next : theConcept.getChildren()) { if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { - myConceptParentChildLinkDao.save(next); + saveConceptLink(next); } else { myConceptLinksToSaveLater.add(next); } @@ -255,6 +253,44 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { } + private void saveConceptLink(TermConceptParentChildLink next) { + if (next.getId() == null) { + myConceptParentChildLinkDao.save(next); + } + } + + private int saveConcept(TermConcept theConcept) { + int retVal = 0; + retVal += ensureParentsSaved(theConcept.getParents()); + if (theConcept.getId() == null || theConcept.getIndexStatus() == null) { + retVal++; + theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); + myConceptDao.saveAndFlush(theConcept); + } + + ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId()); + return retVal; + } + + private int ensureParentsSaved(Collection theParents) { + ourLog.trace("Checking {} parents", theParents.size()); + int retVal = 0; + + for (TermConceptParentChildLink nextLink : theParents) { + if (nextLink.getRelationshipType() == RelationshipTypeEnum.ISA) { + TermConcept nextParent = nextLink.getParent(); + retVal += ensureParentsSaved(nextParent.getParents()); + if (nextParent.getId() == null) { + myConceptDao.saveAndFlush(nextParent); + retVal++; + ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId()); + } + } + } + + return retVal; + } + private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { if (theNext.getCodeSystem() != null) { return; @@ -269,31 +305,80 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc { @Transactional(propagation=Propagation.REQUIRED) @Override public synchronized void saveDeferred() { - if (!myProcessDeferred || ((myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()))) { + if (!myProcessDeferred) { + return; + } else if (myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()) { + processReindexing(); return; } int codeCount = 0, relCount = 0; + StopWatch stopwatch = new StopWatch(); int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size()); ourLog.info("Saving {} deferred concepts...", count); while (codeCount < count && myConceptsToSaveLater.size() > 0) { TermConcept next = myConceptsToSaveLater.remove(0); - myConceptDao.save(next); - codeCount++; + codeCount += saveConcept(next); } + if (codeCount > 0) { + ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)", new Object[] {codeCount, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); + } + if (codeCount == 0) { count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size()); ourLog.info("Saving {} deferred concept relationships...", count); while (relCount < count && myConceptLinksToSaveLater.size() > 0) { TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0); - myConceptParentChildLinkDao.save(next); + saveConceptLink(next); relCount++; } } - ourLog.info("Saved {} deferred concepts ({} remain) and {} deferred relationships ({} remain)", new Object[] {codeCount, myConceptsToSaveLater.size(), relCount, myConceptLinksToSaveLater.size()}); + if (relCount > 0) { + ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)", new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); + } + + if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) { + ourLog.info("All deferred concepts and relationships have now been synchronized to the database"); + } + } + + @Autowired + private PlatformTransactionManager myTransactionMgr; + + private void processReindexing() { + if (System.currentTimeMillis() < myNextReindexPass) { + return; + } + + TransactionTemplate tt = new TransactionTemplate(myTransactionMgr); + tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); + tt.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theArg0) { + int maxResult = 1000; + Page resources = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult)); + if (resources.hasContent() == false) { + myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE; + return; + } + + ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements()); + + int count = 0; + StopWatch stopwatch = new StopWatch(); + + for (TermConcept resourceTable : resources) { + saveConcept(resourceTable); + count++; + } + + ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) }); + } + }); + } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 8a78be41460..11c2fcc430c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Set; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryWrapperFilter; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.query.dsl.BooleanJunction; @@ -78,56 +79,38 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I @Autowired private ValueSetExpander myValueSetExpander; - @Override - public List expandValueSet(String theValueSet) { - ValueSet source = new ValueSet(); - source.getCompose().addImport(theValueSet); - try { - ArrayList retVal = new ArrayList(); - - ValueSetExpansionOutcome outcome = myValueSetExpander.expand(source); - for (ValueSetExpansionContainsComponent next : outcome.getValueset().getExpansion().getContains()) { - retVal.add(new VersionIndependentConcept(next.getSystem(), next.getCode())); - } - - return retVal; - - } catch (Exception e) { - throw new InternalErrorException(e); + private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set addedCodes, TermConcept nextConcept) { + if (addedCodes.add(nextConcept.getCode())) { + ValueSetExpansionContainsComponent contains = retVal.addContains(); + contains.setCode(nextConcept.getCode()); + contains.setSystem(system); + contains.setDisplay(nextConcept.getDisplay()); } - } - @Override - @Transactional(propagation=Propagation.REQUIRED) - public void storeNewCodeSystemVersion(String theSystem, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails) { - CodeSystem cs = new org.hl7.fhir.dstu3.model.CodeSystem(); - cs.setUrl(theSystem); - cs.setContent(CodeSystemContentMode.NOTPRESENT); - - DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails); - IIdType csId = createOutcome.getId().toUnqualifiedVersionless(); - if (createOutcome.getCreated() != Boolean.TRUE) { - CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails); - csId = myCodeSystemResourceDao.update(existing, theRequestDetails).getId(); - - ourLog.info("Created new version of CodeSystem, got ID: {}", csId.toUnqualified().getValue()); - } - - ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId); - Long codeSystemResourcePid = resource.getId(); - - ourLog.info("CodeSystem resource has ID: {}", csId.getValue()); - - theCodeSystemVersion.setResource(resource); - theCodeSystemVersion.setResourceVersionId(resource.getVersion()); - super.storeNewCodeSystemVersion(codeSystemResourcePid, theSystem, theCodeSystemVersion); + private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ConceptSetFilterComponent nextFilter) { + bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); + } + private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction bool, ConceptSetFilterComponent nextFilter) { + //@formatter:off + Query textQuery = qb + .phrase() + .withSlop(2) + .onField("myDisplay").boostedTo(4.0f) + .andField("myDisplayEdgeNGram").boostedTo(2.0f) + //.andField("myDisplayNGram").boostedTo(1.0f) + //.andField("myDisplayPhonetic").boostedTo(0.5f) + .sentence(nextFilter.getValue().toLowerCase()).createQuery(); + bool.must(textQuery); + //@formatter:on } @Override public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { String system = theInclude.getSystem(); + ourLog.info("Starting expansion around code system: {}", system); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); TermCodeSystemVersion csv = cs.getCurrentVersion(); @@ -167,27 +150,40 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery()); for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) { - if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) { - if (isNotBlank(nextFilter.getValue())) { - bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); - } - } else if (nextFilter.getOp() == FilterOperator.ISA) { - if (isNotBlank(nextFilter.getValue())) { + if (isNotBlank(nextFilter.getValue())) { + if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) { + addDisplayFilterExact(qb, bool, nextFilter); + } else if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) { + if (nextFilter.getValue().trim().contains(" ")) { + addDisplayFilterExact(qb, bool, nextFilter); + } else { + addDisplayFilterInexact(qb, bool, nextFilter); + } + } else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) { TermConcept code = super.findCode(system, nextFilter.getValue()); + if (code == null) { + throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue()); + } + + ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay()); bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); + } else { + throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]"); } - } else { - throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]"); } } Query luceneQuery = bool.createQuery(); FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class); + jpaQuery.setMaxResults(1000); + @SuppressWarnings("unchecked") List result = jpaQuery.getResultList(); for (TermConcept nextConcept : result) { addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept); } + + retVal.setTotal(jpaQuery.getResultSize()); } if (!haveIncludeCriteria) { @@ -196,19 +192,29 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept); } } - - retVal.setTotal(retVal.getContains().size()); - + + return retVal; } - private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set addedCodes, TermConcept nextConcept) { - if (addedCodes.add(nextConcept.getCode())) { - ValueSetExpansionContainsComponent contains = retVal.addContains(); - contains.setCode(nextConcept.getCode()); - contains.setSystem(system); - contains.setDisplay(nextConcept.getDisplay()); + @Override + public List expandValueSet(String theValueSet) { + ValueSet source = new ValueSet(); + source.getCompose().addImport(theValueSet); + try { + ArrayList retVal = new ArrayList(); + + ValueSetExpansionOutcome outcome = myValueSetExpander.expand(source); + for (ValueSetExpansionContainsComponent next : outcome.getValueset().getExpansion().getContains()) { + retVal.add(new VersionIndependentConcept(next.getSystem(), next.getCode())); + } + + return retVal; + + } catch (Exception e) { + throw new InternalErrorException(e); } + } @Override @@ -238,6 +244,33 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I return super.supportsSystem(theSystem); } + @Override + @Transactional(propagation = Propagation.REQUIRED) + public void storeNewCodeSystemVersion(String theSystem, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails) { + CodeSystem cs = new org.hl7.fhir.dstu3.model.CodeSystem(); + cs.setUrl(theSystem); + cs.setContent(CodeSystemContentMode.NOTPRESENT); + + DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails); + IIdType csId = createOutcome.getId().toUnqualifiedVersionless(); + if (createOutcome.getCreated() != Boolean.TRUE) { + CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails); + csId = myCodeSystemResourceDao.update(existing, theRequestDetails).getId(); + + ourLog.info("Created new version of CodeSystem, got ID: {}", csId.toUnqualified().getValue()); + } + + ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId); + Long codeSystemResourcePid = resource.getId(); + + ourLog.info("CodeSystem resource has ID: {}", csId.getValue()); + + theCodeSystemVersion.setResource(resource); + theCodeSystemVersion.setResourceVersionId(resource.getVersion()); + super.storeNewCodeSystemVersion(codeSystemResourcePid, theSystem, theCodeSystemVersion); + + } + @CoverageIgnore @Override public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java index 7f4b6b18c03..db10be4c5fa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvc.java @@ -24,15 +24,12 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -47,7 +44,6 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.csv.QuoteMode; -import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -102,6 +98,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc { } ourLog.info(b.toString(), theConcept.getCode()); childIter.remove(); + nextChild.getParents().remove(next); + } else { dropCircularRefs(nextChild, theChain, theCode2concept, theCircularCounter); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/StopWatch.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/StopWatch.java index d66e209a40b..4471fb4e4b8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/StopWatch.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/StopWatch.java @@ -32,4 +32,14 @@ public class StopWatch { return retVal; } + public long getMillis() { + long now = System.currentTimeMillis(); + long retVal = now - myStarted; + return retVal; + } + + public double getMillisPerOperation(int theNumOperations) { + return ((double)getMillis()) / Math.max(1.0, theNumOperations); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu2.java index a1e4ef0d4d1..178060961dd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu2.java @@ -101,7 +101,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu2 extends Interc if (requestId != null && requestId.hasIdPart()) { Subscription existing; try { - existing = myDao.read(requestId, new ServletRequestDetails()); + existing = myDao.read(requestId, null); SubscriptionStatusEnum existingStatus = existing.getStatusElement().getValueAsEnum(); if (existingStatus != newStatus) { verifyActiveStatus(subscription, newStatus, existingStatus); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu3.java index f485f2af619..ccf80bc9fb1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/SubscriptionsRequireManualActivationInterceptorDstu3.java @@ -38,7 +38,6 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.CoverageIgnore; /** @@ -102,7 +101,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu3 extends Interc if (requestId != null && requestId.hasIdPart()) { Subscription existing; try { - existing = myDao.read(requestId, new ServletRequestDetails()); + existing = myDao.read(requestId, null); SubscriptionStatus existingStatus = existing.getStatusElement().getValue(); if (existingStatus != newStatus) { verifyActiveStatus(subscription, newStatus, existingStatus); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SubscriptionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SubscriptionTest.java index c1a9ca5128c..3ed297540d3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SubscriptionTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SubscriptionTest.java @@ -39,7 +39,6 @@ import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu2SubscriptionTest extends BaseJpaDstu2Test { @@ -390,7 +389,7 @@ public class FhirResourceDaoDstu2SubscriptionTest extends BaseJpaDstu2Test { */ mySystemDao.markAllResourcesForReindexing(); - mySystemDao.performReindexingPass(100, mySrd); + mySystemDao.performReindexingPass(100); assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index f61d58f1d43..ff577c54a84 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -16,7 +16,6 @@ import org.junit.Before; import org.junit.Test; import org.springframework.transaction.annotation.Transactional; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; @@ -25,7 +24,6 @@ import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { @@ -58,7 +56,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = null; CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertFalse(result.isResult()); } @@ -71,7 +69,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = null; CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure--expiration", result.getDisplay()); } @@ -85,7 +83,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = null; CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -99,7 +97,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = new StringDt("Systolic blood pressure at First encounterXXXX"); CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertFalse(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -113,7 +111,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = new StringDt("Systolic blood pressure at First encounter"); CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -127,7 +125,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = null; CodingDt coding = null; CodeableConceptDt codeableConcept = new CodeableConceptDt("http://loinc.org", "11378-7"); - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -141,7 +139,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { StringDt display = null; CodingDt coding = null; CodeableConceptDt codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -150,7 +148,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { public void testExpandById() throws IOException { String resp; - ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null); + ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); // @formatter:off @@ -175,7 +173,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { * Filter with display name */ - expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); + expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); //@formatter:off @@ -188,7 +186,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { * Filter with code */ - expanded = myValueSetDao.expand(myExtensionalVsId, ("11378")); + expanded = myValueSetDao.expand(myExtensionalVsId, ("11378"), mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); //@formatter:off diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index c0952b4aa4e..ec59edb957e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -101,7 +101,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { }); assertEquals(null, entity.getIndexStatus()); - mySystemDao.performReindexingPass(null, mySrd); + mySystemDao.performReindexingPass(null); entity = new TransactionTemplate(myTxManager).execute(new TransactionCallback() { @Override @@ -112,7 +112,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { assertEquals(Long.valueOf(1), entity.getIndexStatus()); // Just make sure this doesn't cause a choke - mySystemDao.performReindexingPass(100000, mySrd); + mySystemDao.performReindexingPass(100000); // Try making the resource unparseable @@ -134,7 +134,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } }); - mySystemDao.performReindexingPass(null, mySrd); + mySystemDao.performReindexingPass(null); entity = new TransactionTemplate(myTxManager).execute(new TransactionCallback() { @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java index 3da1eaefb26..84751e66e63 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java @@ -81,7 +81,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test { } private ServletRequestDetails mockSrd() { - return new ServletRequestDetails(); + return mySrd; } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SubscriptionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SubscriptionTest.java index 820a0b52d1e..2174d3d2581 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SubscriptionTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SubscriptionTest.java @@ -390,7 +390,7 @@ public class FhirResourceDaoDstu3SubscriptionTest extends BaseJpaDstu3Test { */ mySystemDao.markAllResourcesForReindexing(); - mySystemDao.performReindexingPass(100, mySrd); + mySystemDao.performReindexingPass(100); assertEquals(0, mySubscriptionDao.pollForNewUndeliveredResources()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 509b270e921..0b6b82eb461 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsStringIgnoringCase; import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -37,147 +38,30 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { - private static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; - private static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3TerminologyTest.class); + public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; + public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + @After + public void after() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); } - @Test - public void testCodeSystemWithDefinedCodes() { - //@formatter:off - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystemContentMode.COMPLETE); - codeSystem - .addConcept().setCode("A").setDisplay("Code A") - .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA")) - .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); - codeSystem - .addConcept().setCode("B").setDisplay("Code A") - .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code AA")) - .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code AB")); - //@formatter:on - - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); - - Set codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A"); - assertThat(toCodes(codes), containsInAnyOrder("A", "AA", "AB")); - + @Before + public void before() { + myDaoConfig.setMaximumExpansionSize(5000); } - @Test - @Ignore - public void testSearchCodeInEmptyValueSet() { - ValueSet valueSet = new ValueSet(); - valueSet.setUrl(URL_MY_VALUE_SET); - myValueSetDao.create(valueSet, mySrd); - - Observation obsAA = new Observation(); - obsAA.setStatus(ObservationStatus.FINAL); - obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); - myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsBA = new Observation(); - obsBA.setStatus(ObservationStatus.FINAL); - obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); - myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsCA = new Observation(); - obsCA.setStatus(ObservationStatus.FINAL); - obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); - myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params; - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - params.add(Observation.SP_STATUS, new TokenParam(null, "final")); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - } - - @Test - public void testSearchCodeInLocalCodesystem() { - createLocalCsAndVs(); - - Observation obsAA = new Observation(); - obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); - IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsBA = new Observation(); - obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); - IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsCA = new Observation(); - obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); - IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue(), idBA.getValue())); - - } - - @Test - public void testSearchCodeInExternalCodesystem() { - createExternalCsAndLocalVs(); - - Observation obsPA = new Observation(); - obsPA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("ParentA"); - IIdType idPA = myObservationDao.create(obsPA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsAAA = new Observation(); - obsAAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAA"); - IIdType idAAA = myObservationDao.create(obsAAA, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsAAB = new Observation(); - obsAAB.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAB"); - IIdType idAAB = myObservationDao.create(obsAAB, mySrd).getId().toUnqualifiedVersionless(); - - Observation obsCA = new Observation(); - obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); - IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAAA.getValue(), idAAB.getValue())); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue())); - - params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue(), idAAA.getValue(), idAAB.getValue())); - - } - - - private void createExternalCsAndLocalVs() { - CodeSystem codeSystem = createExternalCs(); - - createLocalVs(codeSystem); - } - - private CodeSystem createExternalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); @@ -203,179 +87,97 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); cs.getConcepts().add(parentB); + TermConcept childBA = new TermConcept(cs, "childBA").setDisplay("Child BA"); + childBA.addChild(childAAB, RelationshipTypeEnum.ISA); + parentB.addChild(childBA, RelationshipTypeEnum.ISA); + + TermConcept parentC = new TermConcept(cs, "ParentC").setDisplay("Parent C"); + cs.getConcepts().add(parentC); + + TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); + parentC.addChild(childCA, RelationshipTypeEnum.ISA); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); return codeSystem; } + private void createExternalCsAndLocalVs() { + CodeSystem codeSystem = createExternalCs(); + + createLocalVs(codeSystem); + } + + private void createLocalCsAndVs() { + //@formatter:off + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") + .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) + ) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code B") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); + //@formatter:on + myCodeSystemDao.create(codeSystem, mySrd); + + createLocalVs(codeSystem); + } + + + private void createLocalVs(CodeSystem codeSystem) { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl()); + myValueSetDao.create(valueSet, mySrd); + } + + @Test - public void testSearchCodeBelowAndAboveUnknownCodeSystem() { + public void testCodeSystemCreateDuplicateFails() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - SearchParameterMap params = new SearchParameterMap(); + codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + try { + myCodeSystemDao.create(codeSystem, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage()); + } + } + + @Test + public void testCodeSystemWithDefinedCodes() { + //@formatter:off + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA")) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code AA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code AB")); + //@formatter:on - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); - - params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + Set codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A"); + assertThat(toCodes(codes), containsInAnyOrder("A", "AA", "AB")); } - @Test - public void testSearchCodeInFhirCodesystem() { - createLocalCsAndVs(); - - AuditEvent aeIn1 = new AuditEvent(); - aeIn1.getType().setSystem("http://nema.org/dicom/dicm").setCode("110102"); - IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless(); - - AuditEvent aeIn2 = new AuditEvent(); - aeIn2.getType().setSystem("http://hl7.org/fhir/audit-event-type").setCode("rest"); - IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless(); - - AuditEvent aeOut1 = new AuditEvent(); - aeOut1.getType().setSystem("http://example.com").setCode("foo"); - IIdType idOut1 = myAuditEventDao.create(aeOut1, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap params = new SearchParameterMap(); - params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), containsInAnyOrder(idIn1.getValue(), idIn2.getValue())); - - params = new SearchParameterMap(); - params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/v3-PurposeOfUse").setModifier(TokenParamModifier.IN)); - assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty()); - } - - /** - * Can't currently abort costly - */ - @Test - @Ignore - public void testRefuseCostlyExpansionFhirCodesystem() { - createLocalCsAndVs(); - myDaoConfig.setMaximumExpansionSize(1); - - SearchParameterMap params = new SearchParameterMap(); - params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN)); - try { - myAuditEventDao.search(params); - fail(); - } catch (InvalidRequestException e) { - assertEquals("", e.getMessage()); - } - } - - @Test - public void testRefuseCostlyExpansionLocalCodesystem() { - createLocalCsAndVs(); - myDaoConfig.setMaximumExpansionSize(1); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.ABOVE)); - try { - myObservationDao.search(params); - fail(); - } catch (InvalidRequestException e) { - assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage()); - } - } - - @Test - public void testExpandWithSystemAndCodesInLocalValueSet() { - createLocalCsAndVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addConcept().setCode("A"); - include.addConcept().setCode("AA"); - include.addConcept().setCode("AAA"); - include.addConcept().setCode("AB"); - - ValueSet result = myValueSetDao.expand(vs, null); - - String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); - ourLog.info(encoded); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("A", "AA", "AAA", "AB")); - - int idx = codes.indexOf("AAA"); - assertEquals("AAA", result.getExpansion().getContains().get(idx).getCode()); - assertEquals("Code AAA", result.getExpansion().getContains().get(idx).getDisplay()); - assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); -// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical"); -// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical"); -// - } - - - - @Test - public void testExpandWithSystemAndCodesAndFilterInExternalValueSet() { - createExternalCsAndLocalVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - include.addConcept().setCode("ParentA"); - include.addConcept().setCode("childAA"); - include.addConcept().setCode("childAAA"); - - include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B"); - - ValueSet result = myValueSetDao.expand(vs, null); - - String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); - ourLog.info(encoded); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "ParentB")); - - int idx = codes.indexOf("childAA"); - assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode()); - assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay()); - assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); - } - - @Test - public void testIndexingIsDeferredForLargeCodeSystems() { - myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); - - myTermSvc.setProcessDeferred(false); - - createExternalCsAndLocalVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); - - include.addFilter().setProperty("display").setOp(FilterOperator.ISA).setValue("ParentA"); - - ValueSet result = myValueSetDao.expand(vs, null); - String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); - ourLog.info(encoded); - - assertEquals(0, result.getExpansion().getContains().size()); - - myTermSvc.setProcessDeferred(true); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - myTermSvc.saveDeferred(); - - result = myValueSetDao.expand(vs, null); - encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); - ourLog.info(encoded); - assertEquals(4, result.getExpansion().getContains().size()); - - } - @Test public void testExpandWithExcludeInExternalValueSet() { createExternalCsAndLocalVs(); @@ -395,8 +197,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { ourLog.info(encoded); ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB")); - + assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA")); } @Test @@ -421,34 +222,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } } - @Test - public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() { - createLocalCsAndVs(); - - ValueSet vs = new ValueSet(); - ConceptSetComponent include = vs.getCompose().addInclude(); - include.setSystem(URL_MY_CODE_SYSTEM); -// include.addConcept().setCode("A"); -// include.addConcept().setCode("AA"); -// include.addConcept().setCode("AAA"); -// include.addConcept().setCode("AB"); - - include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("AAA"); - - ValueSet result = myValueSetDao.expand(vs, null); - - String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); - ourLog.info(encoded); - - ArrayList codes = toCodesContains(result.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("AAA")); - - assertEquals("AAA", result.getExpansion().getContains().get(0).getCode()); - assertEquals("Code AAA", result.getExpansion().getContains().get(0).getDisplay()); - assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(0).getSystem()); -// - } - @Test public void testExpandWithNoResultsInLocalValueSet1() { createLocalCsAndVs(); @@ -484,14 +257,202 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } } - private ArrayList toCodesContains(List theContains) { - ArrayList retVal = new ArrayList(); - for (ValueSetExpansionContainsComponent next : theContains) { - retVal.add(next.getCode()); - } - return retVal; + + + @Test + public void testExpandWithSystemAndCodesAndFilterInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("ParentA"); + include.addConcept().setCode("childAA"); + include.addConcept().setCode("childAAA"); + + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B"); + + ValueSet result = myValueSetDao.expand(vs, null); + + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "ParentB")); + + int idx = codes.indexOf("childAA"); + assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode()); + assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay()); + assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); } + @Test + public void testExpandWithDisplayInExternalValueSetFuzzyMatching() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("parent a"); + ValueSet result = myValueSetDao.expand(vs, null); + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentA")); + + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("pare"); + result = myValueSetDao.expand(vs, null); + encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "ParentC")); + + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setProperty("display:exact").setOp(FilterOperator.EQUAL).setValue("pare"); + result = myValueSetDao.expand(vs, null); + encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, empty()); + + } + + @Test + public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() { + createLocalCsAndVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); +// include.addConcept().setCode("A"); +// include.addConcept().setCode("AA"); +// include.addConcept().setCode("AAA"); +// include.addConcept().setCode("AB"); + + include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("AAA"); + + ValueSet result = myValueSetDao.expand(vs, null); + + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("AAA")); + + assertEquals("AAA", result.getExpansion().getContains().get(0).getCode()); + assertEquals("Code AAA", result.getExpansion().getContains().get(0).getDisplay()); + assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(0).getSystem()); +// + } + + @Test + public void testExpandWithSystemAndCodesInLocalValueSet() { + createLocalCsAndVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("A"); + include.addConcept().setCode("AA"); + include.addConcept().setCode("AAA"); + include.addConcept().setCode("AB"); + + ValueSet result = myValueSetDao.expand(vs, null); + + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("A", "AA", "AAA", "AB")); + + int idx = codes.indexOf("AAA"); + assertEquals("AAA", result.getExpansion().getContains().get(idx).getCode()); + assertEquals("Code AAA", result.getExpansion().getContains().get(idx).getDisplay()); + assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem()); +// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical"); +// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical"); +// + } + + @Test + public void testIndexingIsDeferredForLargeCodeSystems() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); + + myTermSvc.setProcessDeferred(false); + + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); + + ValueSet result = myValueSetDao.expand(vs, null); + String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + assertEquals(0, result.getExpansion().getContains().size()); + + myTermSvc.setProcessDeferred(true); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); + result = myValueSetDao.expand(vs, null); + encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result); + ourLog.info(encoded); + + assertEquals(4, result.getExpansion().getContains().size()); + + assertThat(encoded, containsStringIgnoringCase("")); + } + + /** + * Can't currently abort costly + */ + @Test + @Ignore + public void testRefuseCostlyExpansionFhirCodesystem() { + createLocalCsAndVs(); + myDaoConfig.setMaximumExpansionSize(1); + + SearchParameterMap params = new SearchParameterMap(); + params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN)); + try { + myAuditEventDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("", e.getMessage()); + } + } + + @Test + public void testRefuseCostlyExpansionLocalCodesystem() { + createLocalCsAndVs(); + myDaoConfig.setMaximumExpansionSize(1); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.ABOVE)); + try { + myObservationDao.search(params); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage()); + } + } @Test public void testSearchCodeAboveLocalCodesystem() { @@ -519,16 +480,23 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } - @Before - public void before() { - myDaoConfig.setMaximumExpansionSize(5000); + @Test + public void testSearchCodeBelowAndAboveUnknownCodeSystem() { + + SearchParameterMap params = new SearchParameterMap(); + + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + } - @After - public void after() { - myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); - } - + @Test public void testSearchCodeBelowLocalCodesystem() { createLocalCsAndVs(); @@ -555,52 +523,134 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { } - private void createLocalCsAndVs() { - //@formatter:off - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystemContentMode.COMPLETE); - codeSystem - .addConcept().setCode("A").setDisplay("Code A") - .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") - .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) - ) - .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); - codeSystem - .addConcept().setCode("B").setDisplay("Code B") - .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) - .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); - //@formatter:on - myCodeSystemDao.create(codeSystem, new ServletRequestDetails()); + @Test + @Ignore + public void testSearchCodeInEmptyValueSet() { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + myValueSetDao.create(valueSet, mySrd); + + Observation obsAA = new Observation(); + obsAA.setStatus(ObservationStatus.FINAL); + obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); + myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); - createLocalVs(codeSystem); + Observation obsBA = new Observation(); + obsBA.setStatus(ObservationStatus.FINAL); + obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); + myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsCA = new Observation(); + obsCA.setStatus(ObservationStatus.FINAL); + obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); + myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params; + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + params.add(Observation.SP_STATUS, new TokenParam(null, "final")); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty()); + } + + @Test + public void testSearchCodeInExternalCodesystem() { + createExternalCsAndLocalVs(); + + Observation obsPA = new Observation(); + obsPA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("ParentA"); + IIdType idPA = myObservationDao.create(obsPA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsAAA = new Observation(); + obsAAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAA"); + IIdType idAAA = myObservationDao.create(obsAAA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsAAB = new Observation(); + obsAAB.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAB"); + IIdType idAAB = myObservationDao.create(obsAAB, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsCA = new Observation(); + obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); + IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAAA.getValue(), idAAB.getValue())); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue())); + + params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue(), idAAA.getValue(), idAAB.getValue())); + + } + + @Test + public void testSearchCodeInFhirCodesystem() { + createLocalCsAndVs(); + + AuditEvent aeIn1 = new AuditEvent(); + aeIn1.getType().setSystem("http://nema.org/dicom/dicm").setCode("110102"); + IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless(); + + AuditEvent aeIn2 = new AuditEvent(); + aeIn2.getType().setSystem("http://hl7.org/fhir/audit-event-type").setCode("rest"); + IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless(); + + AuditEvent aeOut1 = new AuditEvent(); + aeOut1.getType().setSystem("http://example.com").setCode("foo"); + IIdType idOut1 = myAuditEventDao.create(aeOut1, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), containsInAnyOrder(idIn1.getValue(), idIn2.getValue())); + + params = new SearchParameterMap(); + params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/v3-PurposeOfUse").setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty()); + } + + @Test + public void testSearchCodeInLocalCodesystem() { + createLocalCsAndVs(); + + Observation obsAA = new Observation(); + obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA"); + IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsBA = new Observation(); + obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA"); + IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless(); + + Observation obsCA = new Observation(); + obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA"); + IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue(), idBA.getValue())); + } - private void createLocalVs(CodeSystem codeSystem) { - ValueSet valueSet = new ValueSet(); - valueSet.setUrl(URL_MY_VALUE_SET); - valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl()); - myValueSetDao.create(valueSet, mySrd); + private ArrayList toCodesContains(List theContains) { + ArrayList retVal = new ArrayList(); + for (ValueSetExpansionContainsComponent next : theContains) { + retVal.add(next.getCode()); + } + return retVal; } - @Test - public void testCodeSystemCreateDuplicateFails() { - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystemContentMode.COMPLETE); - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); - - codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystemContentMode.COMPLETE); - try { - myCodeSystemDao.create(codeSystem, new ServletRequestDetails()); - fail(); - } catch (UnprocessableEntityException e) { - assertEquals("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage()); - } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 11e2dacf5e9..5f603e423e3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -1493,6 +1493,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { for (int i = 0; i < 10; i++) { Thread.sleep(100); preDates.add(new Date()); + Thread.sleep(100); patient.setId(id); patient.getName().get(0).getFamily().get(0).setValue(methodName + "_i"); ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java index fa2894d7343..3976b73c99b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java @@ -26,7 +26,6 @@ import org.junit.Test; import org.springframework.transaction.annotation.Transactional; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { @@ -62,7 +61,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertFalse(result.isResult()); } @@ -75,7 +74,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure--expiration", result.getDisplay()); } @@ -89,7 +88,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -103,7 +102,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = new StringType("Systolic blood pressure at First encounterXXXX"); Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertFalse(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -117,7 +116,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = new StringType("Systolic blood pressure at First encounter"); Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -132,7 +131,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { Coding coding = null; CodeableConcept codeableConcept = new CodeableConcept(); codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7"); - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -146,7 +145,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { StringType display = null; Coding coding = null; CodeableConcept codeableConcept = null; - ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); + ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isResult()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } @@ -155,7 +154,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { public void testExpandById() throws IOException { String resp; - ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null); + ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); assertThat(resp, containsString("")); @@ -176,7 +175,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { * Filter with display name */ - expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); + expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); //@formatter:off diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 6e2a2243d85..94796de42eb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -191,7 +191,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { }); assertEquals(null, entity.getIndexStatus()); - mySystemDao.performReindexingPass(null, mySrd); + mySystemDao.performReindexingPass(null); entity = new TransactionTemplate(myTxManager).execute(new TransactionCallback() { @Override @@ -202,7 +202,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals(Long.valueOf(1), entity.getIndexStatus()); // Just make sure this doesn't cause a choke - mySystemDao.performReindexingPass(100000, mySrd); + mySystemDao.performReindexingPass(100000); // Try making the resource unparseable @@ -224,7 +224,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { } }); - mySystemDao.performReindexingPass(null, mySrd); + mySystemDao.performReindexingPass(null); entity = new TransactionTemplate(myTxManager).execute(new TransactionCallback() { @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java index 36a2a3c7616..6da4f7e8448 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2Test { @@ -251,7 +250,7 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2 expanded = (ValueSet) respParam.getParameter().get(0).getResource(); //@formatter:on - expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); + expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); ourLog.info(resp); //@formatter:off diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java index 8581ae9f1c0..d5e6d8dff6c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 98ce712d9e4..36556c58b34 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -1,9 +1,12 @@ package ca.uhn.fhir.jpa.provider.dstu3; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.*; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -16,6 +19,10 @@ import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; +import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Before; @@ -23,6 +30,10 @@ import org.junit.Ignore; import org.junit.Test; import org.springframework.transaction.annotation.Transactional; +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.TestUtil; @@ -30,6 +41,8 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3ValueSetTest.class); private IIdType myExtensionalVsId; + private IIdType myLocalValueSetId; + private ValueSet myLocalVs; @AfterClass public static void afterClassClearContext() { @@ -77,6 +90,140 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } + @Test + public void testExpandLocalVsAgainstExternalCs() throws IOException { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId); + + //@formatter:off + Parameters respParam = ourClient + .operation() + .onInstance(myLocalValueSetId) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsWithUnknownCode() throws IOException { + createExternalCsAndLocalVsWithUnknownCode(); + assertNotNull(myLocalValueSetId); + + //@formatter:off + try { + ourClient + .operation() + .onInstance(myLocalValueSetId) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Invalid filter criteria - code does not exist: {http://example.com/my_code_system}childFOOOOOOO", e.getMessage()); + } + //@formatter:on + } + + @Test + public void testExpandInlineVsAgainstBuiltInCs() throws IOException { + createLocalVsPointingAtBuiltInCodeSystem(); + assertNotNull(myLocalValueSetId); + + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + } + + @Test + public void testExpandLocalVsAgainstBuiltInCs() throws IOException { + createLocalVsPointingAtBuiltInCodeSystem(); + assertNotNull(myLocalValueSetId); + + //@formatter:off + Parameters respParam = ourClient + .operation() + .onInstance(myLocalValueSetId) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + } + + @Test + public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId); + + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "identifier", new UriType(URL_MY_VALUE_SET)) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandInlineVsAgainstExternalCs() throws IOException { + createExternalCsAndLocalVs(); + assertNotNull(myLocalVs); + myLocalVs.setId(""); + + //@formatter:off + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + @Test public void testExpandByIdWithFilter() throws IOException { @@ -259,4 +406,102 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); } + + + + + private CodeSystem createExternalCs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + cs.setResourceVersionId(table.getVersion()); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA"); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA"); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB"); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB"); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); + cs.getConcepts().add(parentB); + + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + return codeSystem; + } + + private void createExternalCsAndLocalVs() { + CodeSystem codeSystem = createExternalCs(); + + createLocalVs(codeSystem); + } + + private void createExternalCsAndLocalVsWithUnknownCode() { + CodeSystem codeSystem = createExternalCs(); + + createLocalVsWithUnknownCode(codeSystem); + } + + private void createLocalCsAndVs() { + //@formatter:off + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") + .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) + ) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code B") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); + //@formatter:on + myCodeSystemDao.create(codeSystem, mySrd); + + createLocalVs(codeSystem); + } + + + private void createLocalVs(CodeSystem codeSystem) { + myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(codeSystem.getUrl()); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childAA"); + myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); + } + + private void createLocalVsWithUnknownCode(CodeSystem codeSystem) { + myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(codeSystem.getUrl()); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); + myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); + } + + private void createLocalVsPointingAtBuiltInCodeSystem() { + myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem("http://hl7.org/fhir/v3/MaritalStatus"); + myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java index 7dc0518d287..aaa04e9926b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java @@ -367,6 +367,19 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test { } } + @Test + public void testMarkResourcesForReindexing() throws Exception { + HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing"); + CloseableHttpResponse http = ourHttpClient.execute(get); + try { + String output = IOUtils.toString(http.getEntity().getContent()); + ourLog.info(output); + assertEquals(200, http.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(http);; + } + } + @Transactional(propagation = Propagation.NEVER) @Test public void testSuggestKeywords() throws Exception { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java index 5d1e08af0db..b4e41aa2c3c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationTest.java @@ -30,10 +30,6 @@ public class TerminologyLoaderSvcIntegrationTest extends BaseJpaDstu3Test { @Test @Ignore public void testLoadAndStoreSnomedCt() { - Map files = new HashMap(); - files.put(TerminologyLoaderSvc.SCT_FILE_CONCEPT, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Concept_Full_INT_20160131.txt")); - files.put(TerminologyLoaderSvc.SCT_FILE_DESCRIPTION, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Description_Full-en_INT_20160131.txt")); - files.put(TerminologyLoaderSvc.SCT_FILE_RELATIONSHIP, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Relationship_Full_INT_20160131.txt")); // myLoader.processSnomedCtFiles(files, mySrd); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java index f8f9f9b1f3c..3a07b2b0578 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcTest.java @@ -1,17 +1,17 @@ package ca.uhn.fhir.jpa.term; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -25,6 +25,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.junit.AfterClass; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -79,6 +80,21 @@ public class TerminologyLoaderSvcTest { mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details); } + + /** + * This is just for trying stuff, it won't run without + * local files external to the git repo + */ + @Ignore + @Test + public void testLoadSnomedCtAgainstRealFile() throws Exception { + byte[] bytes = IOUtils.toByteArray(new FileInputStream("/Users/james/Downloads/SnomedCT_Release_INT_20160131_Full.zip")); + + RequestDetails details = mock(RequestDetails.class); + mySvc.loadSnomedCt(list(bytes), details); + } + + @Test public void testLoadSnomedCt() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplTest.java index 26a905b98c4..6a00ef14a64 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplTest.java @@ -42,7 +42,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); @@ -109,14 +109,14 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test { assertThat(mySystemDao.markAllResourcesForReindexing(), greaterThan(0)); - assertThat(mySystemDao.performReindexingPass(100, mySrd), greaterThan(0)); + assertThat(mySystemDao.performReindexingPass(100), greaterThan(0)); } private IIdType createCodeSystem() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); @@ -173,7 +173,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); @@ -187,7 +187,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test { cs = new TermCodeSystemVersion(); TermConcept parentA = new TermConcept(cs, "ParentA"); cs.getConcepts().add(parentA); - id = myCodeSystemDao.update(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + id = myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified(); table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); @@ -197,7 +197,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test { codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified(); + id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt index 43be7c43d81..9ad27019224 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt +++ b/hapi-fhir-jpaserver-base/src/test/resources/sct/sct2_Concept_Full_INT_20160131.txt @@ -17,3 +17,4 @@ id effectiveTime active moduleId definitionStatusId 207527008 20020131 1 900000000000207008 900000000000074008 207527008 20040731 1 900000000000207008 900000000000073002 207527008 20090731 0 900000000000207008 900000000000074008 +404684003 20040131 1 900000000000207008 900000000000074008 diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu1Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu1Config.java index 8b8896627c2..019ef853c72 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu1Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu1Config.java @@ -10,7 +10,6 @@ import org.hibernate.jpa.HibernatePersistenceProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.orm.jpa.JpaTransactionManager; @@ -19,6 +18,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu1; import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; @Configuration @Import(CommonConfig.class) @@ -38,6 +39,11 @@ public class TestDstu1Config extends BaseJavaConfigDstu1 { return new PropertySourcesPlaceholderConfigurer(); } + @Bean + public IServerInterceptor securityInterceptor() { + return new PublicSecurityInterceptor(); + } + @Bean() public DaoConfig daoConfig() { DaoConfig retVal = new DaoConfig(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index 69380db0ce5..7ab13bed574 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; @Configuration @Import(CommonConfig.class) @@ -46,6 +47,11 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { return new PropertySourcesPlaceholderConfigurer(); } + @Bean + public IServerInterceptor securityInterceptor() { + return new PublicSecurityInterceptor(); + } + @Bean() public DaoConfig daoConfig() { DaoConfig retVal = new DaoConfig(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index c41e4c64935..a7499b3f3bd 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; @Configuration @Import(CommonConfig.class) @@ -57,6 +58,11 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 { return retVal; } + @Bean + public IServerInterceptor securityInterceptor() { + return new PublicSecurityInterceptor(); + } + @Bean(name = "myPersistenceDataSourceDstu3", destroyMethod = "close") @DependsOn("dbServer") public DataSource dataSource() { diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java new file mode 100644 index 00000000000..a4ba1392df6 --- /dev/null +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java @@ -0,0 +1,61 @@ +package ca.uhn.fhirtest.interceptor; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider; +import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; +import ca.uhn.fhir.rest.method.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; +import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; + +public class PublicSecurityInterceptor extends AuthorizationInterceptor { + + private HashSet myTokens; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicSecurityInterceptor.class); + + public PublicSecurityInterceptor() { + String passwordsString = System.getProperty("fhir.tdlpass"); + String[] passwords = passwordsString.split(","); + myTokens = new HashSet(Arrays.asList(passwords)); + + ourLog.info("We have {} valid security tokens", myTokens.size()); + } + + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + String authHeader = theRequestDetails.getHeader("Authorization"); + + //@formatter:off + if (isBlank(authHeader)) { + return new RuleBuilder() + .deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen() + .deny().operation().named(TerminologyUploaderProviderDstu3.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andThen() + .allowAll() + .build(); + } + //@formatter:off + + if (!authHeader.startsWith("Bearer ")) { + throw new ForbiddenOperationException("Invalid bearer token, must be in the form \"Authorization: Bearer [token]\""); + } + + String token = authHeader.substring("Bearer ".length()).trim(); + if (!myTokens.contains(token)) { + ourLog.error("Invalid token '{}' - Valid are: {}", token, myTokens); + throw new ForbiddenOperationException("Unknown/expired bearer token"); + } + + ourLog.info("User logged in with bearer token: " + token.substring(0, 4) + "..."); + + return new RuleBuilder() + .allowAll() + .build(); + } + +} diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java index b210365c6ee..4aaf97c2c57 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java @@ -1,17 +1,35 @@ package ca.uhn.fhir.rest.param; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import java.util.Date; import java.util.TimeZone; import org.junit.Test; +import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; +import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; public class DateParamTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateParamTest.class); + @SuppressWarnings("deprecation") + @Test + public void testConstructors() { + new DateParam(); + new DateParam("2011-01-02"); + new DateParam(ParamPrefixEnum.GREATERTHAN,new Date()); + new DateParam(ParamPrefixEnum.GREATERTHAN,new DateTimeDt("2011-01-02")); + new DateParam(ParamPrefixEnum.GREATERTHAN,InstantDt.withCurrentTime()); + new DateParam(ParamPrefixEnum.GREATERTHAN,"2011-01-02"); + + new DateParam(QuantityCompararatorEnum.GREATERTHAN,new Date()); + new DateParam(QuantityCompararatorEnum.GREATERTHAN,new DateTimeDt("2011-01-02")); + new DateParam(QuantityCompararatorEnum.GREATERTHAN,InstantDt.withCurrentTime()); + new DateParam(QuantityCompararatorEnum.GREATERTHAN,"2011-01-02"); + } + @Test public void testParse() { Date date = new Date(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java index 307e3da1217..bb113db9453 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.rest.server.interceptor.auth; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -32,7 +33,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; @@ -40,11 +40,13 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Observation; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; @@ -55,14 +57,19 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Transaction; import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.method.IRequestOperationCallback; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.server.AddProfileTagEnum; import ca.uhn.fhir.rest.server.Constants; +import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.TestUtil; @@ -158,6 +165,14 @@ public class AuthorizationInterceptorDstu2Test { assertThat(response, containsString("Access denied by rule: Rule 1")); assertEquals(403, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); + status = ourClient.execute(httpGet); + extractResponseAndClose(status); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + } @Test @@ -404,6 +419,15 @@ public class AuthorizationInterceptorDstu2Test { assertThat(response, containsString("Access denied by rule: Default Rule")); assertEquals(403, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertThat(response, containsString("Access denied by rule: Default Rule")); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); } @Test @@ -770,6 +794,7 @@ public class AuthorizationInterceptorDstu2Test { //@formatter:off return new RuleBuilder() .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")).andThen() + .allow("Rule 1b").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1123")).andThen() .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) .build(); //@formatter:on @@ -788,6 +813,17 @@ public class AuthorizationInterceptorDstu2Test { assertEquals(403, status.getStatusLine().getStatusCode()); assertFalse(ourHitMethod); + // Conditional + ourHitMethod = false; + httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + httpPost.addHeader("If-None-Exist", "Patient?foo=bar"); + httpPost.setEntity(createFhirResourceEntity(createPatient(null))); + status = ourClient.execute(httpPost); + response = extractResponseAndClose(status); + assertEquals(ERR403, response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/2"))); @@ -874,6 +910,16 @@ public class AuthorizationInterceptorDstu2Test { assertEquals(201, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); + // Conditional + ourHitMethod = false; + httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); + httpPost.setEntity(createFhirResourceEntity(createPatient(1))); + status = ourClient.execute(httpPost); + response = extractResponseAndClose(status); + assertEquals(ERR403, response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + ourHitMethod = false; httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/10"); httpPost.setEntity(createFhirResourceEntity(createObservation(10, "Patient/1"))); @@ -990,7 +1036,14 @@ public class AuthorizationInterceptorDstu2Test { public static class DummyPatientResourceProvider implements IResourceProvider { @Create() - public MethodOutcome create(@ResourceParam Patient theResource) { + public MethodOutcome create(@ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { + + if (isNotBlank(theConditionalUrl)) { + IdDt actual = new IdDt("Patient", "1123"); + ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); + subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); + } + ourHitMethod = true; theResource.setId("Patient/1/_history/1"); MethodOutcome retVal = new MethodOutcome(); @@ -999,6 +1052,7 @@ public class AuthorizationInterceptorDstu2Test { return retVal; } + @Delete() public MethodOutcome delete(IRequestOperationCallback theRequestOperationCallback, @IdParam IdDt theId) { ourHitMethod = true; @@ -1009,6 +1063,24 @@ public class AuthorizationInterceptorDstu2Test { return retVal; } + @Validate + public MethodOutcome validate(@ResourceParam Patient theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode, + @Validate.Profile String theProfile, RequestDetails theRequestDetails) { + ourHitMethod = true; + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("OK"); + return new MethodOutcome(oo); + } + + @Validate + public MethodOutcome validate(@ResourceParam Patient theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode, + @Validate.Profile String theProfile, RequestDetails theRequestDetails) { + ourHitMethod = true; + OperationOutcome oo = new OperationOutcome(); + oo.addIssue().setDiagnostics("OK"); + return new MethodOutcome(oo); + } + @Override public Class getResourceType() { return Patient.class; @@ -1027,8 +1099,15 @@ public class AuthorizationInterceptorDstu2Test { } @Update() - public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient theResource) { + public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { ourHitMethod = true; + + if (isNotBlank(theConditionalUrl)) { + IdDt actual = new IdDt("Patient", "1123"); + ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); + subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); + } + theResource.setId(theId.withVersion("2")); MethodOutcome retVal = new MethodOutcome(); retVal.setCreated(true); diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java index d5ec543d0c6..cab4c0ea645 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/validation/HapiWorkerContext.java @@ -67,6 +67,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander ValueSetExpansionOutcome vso; try { vso = getExpander().expand(theSource); + } catch (InvalidRequestException e) { + throw e; } catch (Exception e) { throw new InternalErrorException(e); } @@ -125,7 +127,9 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander @Override public ValueSetExpander getExpander() { - return new ValueSetExpanderSimple(this, this); + ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this); + retVal.setMaxExpansionSize(Integer.MAX_VALUE); + return retVal; } @Override diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java index 12091ec9b18..a80d8a9d78b 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/terminologies/ValueSetExpanderSimple.java @@ -70,8 +70,10 @@ public class ValueSetExpanderSimple implements ValueSetExpander { private Set excludeKeys = new HashSet(); private ValueSetExpanderFactory factory; private ValueSet focus; + private int maxExpansionSize = 500; private Map map = new HashMap(); + private int total; public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) { super(); @@ -79,6 +81,10 @@ public class ValueSetExpanderSimple implements ValueSetExpander { this.factory = factory; } + public void setMaxExpansionSize(int theMaxExpansionSize) { + maxExpansionSize = theMaxExpansionSize; + } + private void addCode(String system, String code, String display) { ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); n.setSystem(system); @@ -101,7 +107,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander { } private void addCodes(ValueSetExpansionComponent expand, List params) throws ETooCostly { - if (expand.getContains().size() > 500) + if (expand.getContains().size() > maxExpansionSize) throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); for (ValueSetExpansionParameterComponent p : expand.getParameter()) { if (!existsInParams(params, p.getName(), p.getValue())) @@ -111,6 +117,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander { for (ValueSetExpansionContainsComponent c : expand.getContains()) { addCode(c.getSystem(), c.getCode(), c.getDisplay()); } + + total = expand.getTotal(); } private void excludeCode(String theSystem, String theCode) { @@ -171,6 +179,11 @@ public class ValueSetExpanderSimple implements ValueSetExpander { focus.getExpansion().getContains().add(c); } } + + if (total > 0) { + focus.getExpansion().setTotal(total); + } + return new ValueSetExpansionOutcome(focus, null); } catch (RuntimeException e) { // TODO: we should put something more specific instead of just Exception below, since diff --git a/src/changes/changes.xml b/src/changes/changes.xml index fb95ab2f05e..7f7407480cd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -386,6 +386,11 @@ create a BigDecimal from a double, resulting in weird floating point conversions. Thanks to Craig McClendon for reporting! + + Remove the depdendency on a method from commons-lang3 3.3 which was + causing issues on some Android phones which come with an older version + of this library bundled. Thanks to Paolo Perliti for reporting! + diff --git a/src/site/resources/images/hapi_authorizationinterceptor_read_normal.svg b/src/site/resources/images/hapi_authorizationinterceptor_read_normal.svg new file mode 100644 index 00000000000..5a4bac103c1 --- /dev/null +++ b/src/site/resources/images/hapi_authorizationinterceptor_read_normal.svg @@ -0,0 +1,2 @@ + +
RestfulServer
RestfulServer
Authorization
Interceptor
[Not supported by viewer]
ResourceProvider
(user code)
ResourceProvider<div>(user code)</div>
read/search/etc
read/search/etc
authorize?
authorize?
invoke
invoke
return
return
200 OK
200 OK
authorize?
authorize?
invoke
invoke
return
return
Successful
Read
[Not supported by viewer]
Denied
Read
[Not supported by viewer]
Return value isĀ 
checked before
actually returning
[Not supported by viewer]
Client
Client
read/search/etc
read/search/etc
403 FORBIDDEN
403 FORBIDDEN
\ No newline at end of file diff --git a/src/site/resources/images/hapi_authorizationinterceptor_write_normal.svg b/src/site/resources/images/hapi_authorizationinterceptor_write_normal.svg new file mode 100644 index 00000000000..cb9aee69a3d --- /dev/null +++ b/src/site/resources/images/hapi_authorizationinterceptor_write_normal.svg @@ -0,0 +1,2 @@ + +
RestfulServer
RestfulServer
Client
Client
write
write
Authorization
Interceptor
[Not supported by viewer]
authorize?
authorize?
ResourceProvider
(user code)
ResourceProvider<div>(user code)</div>
return
return
authorize?
authorize?
invoke
invoke
return
return
200 OK
200 OK
write
write
403 FORBIDDEN
403 FORBIDDEN
Successful
Write
[Not supported by viewer]
Denied
Write
[Not supported by viewer]
Operation is checked
before passing
to ResourceProvider
[Not supported by viewer]
\ No newline at end of file diff --git a/src/site/site.xml b/src/site/site.xml index d008088cdac..c66a8366a76 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -3,7 +3,7 @@ xmlns="http://maven.apache.org/DECORATION/1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/DECORATION/1.3.0 http://maven.apache.org/xsd/decoration-1.3.0.xsd" - name="HAPI"> + name="HAPI FHIR"> - + @@ -218,7 +218,7 @@ HAPI FHIR ]]> - http://jamesagnew.github.io/hapi-fhir/ + http://hapifhir.io/ %2$s - HAPI FHIR diff --git a/src/site/xdoc/doc_rest_server_security.xml b/src/site/xdoc/doc_rest_server_security.xml index e201e5d4cbc..ca08cc1cdc8 100644 --- a/src/site/xdoc/doc_rest_server_security.xml +++ b/src/site/xdoc/doc_rest_server_security.xml @@ -118,22 +118,83 @@ the response from the server in order to determine whether "read" operations are legal.

+ + + + +

- This approach has limitations however: If a request has a conditional operation, - such as a delete operation which uses a search URL, or a create operation which - uses an If-None-Exist header, the interceptor will not know the - actual target until the server actually processes the request. + When authorizing a read operation, the AuthorizationInterceptor + always allows client code to execute and generate a response. + It then examines the response that would be returned before + actually returning it to the client, and if rules do not permit + that data to be shown to the client the interceptor aborts the + request. +

+ +

+ Note that there are performance implications to this mechanism, + since an unauthorized user can still cause the server to fetch data + even if they won't get to see it. This mechanism should be comprehensive + however, since it will prevent clients from using various features + in FHIR (e.g. _include or _revinclude) to + "trick" the server into showing them date they shouldn't be allowed to + see. +

+ +

+ See the following diagram for an example of how this works. +

+ + Write Authorization + +
+ + + +

+ Write operations (create, update, etc.) are typically authorized + by the interceptor by examining the parsed URL and making a decision + about whether to authorize the operation before allowing Resource Provider + code to proceed. This means that client code will not have a chance to execute + and create resources that the client does not have permissions to create. +

+ +

+ See the following diagram for an example of how this works. +

+ + Write Authorization + +
+ + + +

+ There are a number of situations where the REST framework doesn't + actually know exactly what operation is going to be performed by + the implementing server code. For example, if your server implements + a conditional update operation, the server might not know + which resource is actually being updated until the server code + is executed.

- For better security, individual resource providers should notify interceptors - about their actual targets in the event of any "write" operations (create, - operations embedded in transactions, etc.) + Because client code is actually determining which resources are + being modified, the server can not automatically apply security + rules against these modifications without being provided hints + from client code.

-

- The mechanism for doing this isn't yet fully documented, this will be improved - over the next release cycle (post 1.5). Please get in touch on our google group - if you want to help! +

+ In this type of situation, it is important to manually + notify the interceptor chain about the "sub-operation" being performed. + The following snippet shows how to notify interceptors about + a conditional create.

+ + + + +