From 3191c907a3fa12404e3d00e78ccf564c21f1fe02 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Thu, 2 Feb 2017 06:23:28 -0500 Subject: [PATCH] Work on JPA --- hapi-fhir-android-realm/.project | 23 ++++++ .../ca/uhn/fhir/i18n/hapi-messages.properties | 3 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 73 ++++++++++++------- .../ca/uhn/fhir/jpa/dao/IFhirResourceDao.java | 15 +++- .../FhirResourceDaoSearchParameterDstu3.java | 15 +++- ...rceProviderCustomSearchParamDstu3Test.java | 56 ++++++++++++++ .../resources/vm/jpa_resource_provider.vm | 3 + 7 files changed, 155 insertions(+), 33 deletions(-) create mode 100644 hapi-fhir-android-realm/.project create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java diff --git a/hapi-fhir-android-realm/.project b/hapi-fhir-android-realm/.project new file mode 100644 index 00000000000..13005c6b849 --- /dev/null +++ b/hapi-fhir-android-realm/.project @@ -0,0 +1,23 @@ + + + hapi-fhir-android-realm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index ff82937d610..636b56fc038 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -43,11 +43,9 @@ ca.uhn.fhir.rest.param.ResourceParameter.noContentTypeInRequest=No Content-Type ca.uhn.fhir.rest.param.ResourceParameter.failedToParseRequest=Failed to parse request body as {0} resource. Error was: {1} ca.uhn.fhir.parser.ParserState.wrongResourceTypeFound=Incorrect resource type found, expected "{0}" but found "{1}" - ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET ca.uhn.fhir.rest.server.RestfulServer.unknownMethod=Invalid request: The FHIR endpoint on this server does not know how to handle {0} operation[{1}] with parameters [{2}] ca.uhn.fhir.rest.server.RestfulServer.rootRequest=This is the base URL of FHIR server. Unable to handle this request, as it does not contain a resource type or operation name. - ca.uhn.fhir.validation.ValidationContext.unableToDetermineEncoding=Unable to determine encoding (e.g. XML / JSON) on validation input. Is this a valid FHIR resource body? ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation @@ -81,6 +79,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.unableToDeleteNotFound=Unable to fin ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulCreate=Successfully created resource "{0}" in {1}ms ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulUpdate=Successfully updated resource "{0}" in {1}ms ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms +ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}". Value search parameters for this search are: {1} ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1} ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1} 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 5057c12612f..4848c204674 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 @@ -57,6 +57,8 @@ import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.method.SearchMethodBinding; +import ca.uhn.fhir.rest.method.SearchMethodBinding.QualifierDetails; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; @@ -73,6 +75,7 @@ public abstract class BaseHapiFhirResourceDao extends B @Autowired private DaoConfig myDaoConfig; + @Autowired protected PlatformTransactionManager myPlatformTransactionManager; @Autowired @@ -86,6 +89,8 @@ public abstract class BaseHapiFhirResourceDao extends B @Autowired() protected ISearchResultDao mySearchResultDao; private String mySecondaryPrimaryKeyParamName; + @Autowired + private ISearchParamRegistry mySerarchParamRegistry; @Autowired() protected IHapiTerminologySvc myTerminologySvc; @@ -662,6 +667,30 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } + @Override + public DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails) { + ResourceTable entityToUpdate = readEntityLatestVersion(theId); + if (theId.hasVersionIdPart()) { + if (theId.getVersionIdPartAsLong() != entityToUpdate.getVersion()) { + throw new ResourceVersionConflictException("Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch"); + } + } + + validateResourceType(entityToUpdate); + + IBaseResource resourceToUpdate = toResource(entityToUpdate, false); + IBaseResource destination; + if (thePatchType == PatchTypeEnum.JSON_PATCH) { + destination = JsonPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody); + } else { + destination = XmlPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody); + } + + @SuppressWarnings("unchecked") + T destinationCasted = (T) destination; + return update(destinationCasted, null, true, theRequestDetails); + } + @PostConstruct public void postConstruct() { RuntimeResourceDefinition def = getContext().getResourceDefinition(myResourceType); @@ -863,9 +892,6 @@ public abstract class BaseHapiFhirResourceDao extends B return search(map); } - @Autowired - private ISearchParamRegistry mySerarchParamRegistry; - @Override public IBundleProvider search(final SearchParameterMap theParams) { // Notify interceptors @@ -988,6 +1014,23 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } + @Override + public void translateRawParameters(Map> theSource, SearchParameterMap theTarget) { + Map searchParams = mySerarchParamRegistry.getActiveSearchParams(getResourceName()); + + Set paramNames = theSource.keySet(); + for (String nextParamName : paramNames) { + QualifierDetails qualifiedParamName = SearchMethodBinding.extractQualifiersFromParameterName(nextParamName); + RuntimeSearchParam param = searchParams.get(qualifiedParamName.getParamName()); + if (param == null) { + String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), new TreeSet(searchParams.keySet())); + throw new InvalidRequestException(msg); + } + + aaaa + } + } + @Override public DaoMethodOutcome update(T theResource) { return update(theResource, null, null); @@ -1078,30 +1121,6 @@ public abstract class BaseHapiFhirResourceDao extends B ourLog.info(msg); return outcome; } - - @Override - public DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails) { - ResourceTable entityToUpdate = readEntityLatestVersion(theId); - if (theId.hasVersionIdPart()) { - if (theId.getVersionIdPartAsLong() != entityToUpdate.getVersion()) { - throw new ResourceVersionConflictException("Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch"); - } - } - - validateResourceType(entityToUpdate); - - IBaseResource resourceToUpdate = toResource(entityToUpdate, false); - IBaseResource destination; - if (thePatchType == PatchTypeEnum.JSON_PATCH) { - destination = JsonPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody); - } else { - destination = XmlPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody); - } - - @SuppressWarnings("unchecked") - T destinationCasted = (T) destination; - return update(destinationCasted, null, true, theRequestDetails); - } @Override 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 4e6469a15ae..5c64e197ff2 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 @@ -39,8 +39,10 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.method.RequestDetails; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IBundleProvider; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; public interface IFhirResourceDao extends IDao { @@ -134,6 +136,8 @@ public interface IFhirResourceDao extends IDao { */ MT metaGetOperation(Class theType, RequestDetails theRequestDetails); + DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails); + Set processMatchUrl(String theMatchUrl); /** @@ -181,6 +185,15 @@ public interface IFhirResourceDao extends IDao { Set searchForIdsWithAndOr(SearchParameterMap theParams); + /** + * Takes a map of incoming raw search parameters and translates/parses them into + * appropriate {@link IQueryParameterType} instances of the appropriate type + * for the given param + * + * @throws InvalidRequestException If any of the parameters are not known + */ + void translateRawParameters(Map> theSource, SearchParameterMap theTarget); + /** * Update a resource - Note that this variant of the method does not take in a {@link RequestDetails} and * therefore can not fire any interceptors. Use only for internal system calls @@ -211,8 +224,6 @@ public interface IFhirResourceDao extends IDao { */ MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequestDetails); - DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails); - // /** // * Invoke the everything operation // */ 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 cf8fd24e65b..b7ac869fbf9 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,17 +29,28 @@ import org.springframework.scheduling.annotation.Scheduled; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3implements IFhirResourceDaoSearchParameter { @Autowired private IFhirSystemDao mySystemDao; + @Override + protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) { + super.validateResourceForStorage(theResource, theEntityToSave); + + if (theResource.getStatus() == null) { + throw new InvalidRequestException("Resource.status is missing or invalid: " + theResource.getStatusElement().getValueAsString()); + } + } + /** * This method is called once per minute to perform any required re-indexing. During most passes this will * just check and find that there are no resources requiring re-indexing. In that case the method just returns - * immediately. If the search finds that some resources require reindexing, the system will do a bunch of - * reindexing and then return. + * immediately. If the search finds that some resources require reindexing, the system will do multiple + * reindexing passes and then return. */ @Override @Scheduled(fixedDelay=DateUtils.MILLIS_PER_MINUTE) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java new file mode 100644 index 00000000000..90c479fa782 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderCustomSearchParamDstu3Test.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.hl7.fhir.dstu3.model.SearchParameter; +import org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Test; + +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.TestUtil; + +public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProviderDstu3Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamDstu3Test.class); + + @Override + @After + public void after() throws Exception { + super.after(); + + myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden()); + } + + @Override + public void before() throws Exception { + super.before(); + } + + @Test + public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException { + SearchParameter sp = new SearchParameter(); + sp.setCode("foo"); + sp.setXpath("Patient.gender"); + sp.setXpathUsage(XPathUsageType.NORMAL); + sp.setTitle("Foo Param"); + + try { + ourClient.create().resource(sp).execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("", e.getMessage()); + } + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + +} diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 212dfb62ee2..df8987b6bf5 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -99,6 +99,9 @@ public class ${className}ResourceProvider extends #end #end + @RawParam + Map> theAdditionalRawParams, + #if ( $version != 'dstu' ) @IncludeParam(reverse=true) Set theRevIncludes,