diff --git a/examples/pom.xml b/examples/pom.xml index 83ae563ebbc..9adf692ef09 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/examples/src/main/java/example/ConverterExamples.java b/examples/src/main/java/example/ConverterExamples.java index 219b59f7a22..710e8338485 100644 --- a/examples/src/main/java/example/ConverterExamples.java +++ b/examples/src/main/java/example/ConverterExamples.java @@ -1,7 +1,7 @@ package example; -import org.hl7.fhir.converter.NullVersionConverterAdvisor30; -import org.hl7.fhir.convertors.*; +import org.hl7.fhir.convertors.conv10_30.Observation10_30; +import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30; import org.hl7.fhir.exceptions.FHIRException; public class ConverterExamples { @@ -9,16 +9,12 @@ public class ConverterExamples { @SuppressWarnings("unused") public void c1020() throws FHIRException { //START SNIPPET: 1020 - // Create a converter - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - // Create an input resource to convert org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); // Convert the resource - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = Observation10_30.convertObservation(input); String context = output.getContext().getReference(); //END SNIPPET: 1020 } @@ -31,7 +27,7 @@ public class ConverterExamples { input.setTitle("My title"); // Convert the resource - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = Questionnaire14_30.convertQuestionnaire(input); String context = output.getTitle(); //END SNIPPET: 1420 } diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index d40f599762d..cfbbacddefa 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 0e7ed584440..129477c4a80 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 1193fac9f34..eb562662816 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -206,65 +206,4 @@ - - - MINI - - - SITE - - - - - - org.apache.maven.plugins - maven-jxr-plugin - - - normal - - jxr - - - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - - checkstyle - - - - - false - ${project.basedir}/../src/checkstyle/checkstyle.xml - - - - - - - - - - diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java index 1ff262c9fa7..5407ac8bd14 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java @@ -62,41 +62,41 @@ public class ParameterUtil { */ public static IQueryParameterAnd parseQueryParams(FhirContext theContext, RestSearchParameterTypeEnum paramType, String theUnqualifiedParamName, List theParameters) { - QueryParameterAndBinder binder = null; + QueryParameterAndBinder binder; switch (paramType) { case COMPOSITE: throw new UnsupportedOperationException(); case DATE: binder = new QueryParameterAndBinder(DateAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case NUMBER: binder = new QueryParameterAndBinder(NumberAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case QUANTITY: binder = new QueryParameterAndBinder(QuantityAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case REFERENCE: binder = new QueryParameterAndBinder(ReferenceAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case STRING: binder = new QueryParameterAndBinder(StringAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case TOKEN: binder = new QueryParameterAndBinder(TokenAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case URI: binder = new QueryParameterAndBinder(UriAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case HAS: binder = new QueryParameterAndBinder(HasAndListParam.class, - Collections.>emptyList()); + Collections.emptyList()); break; case SPECIAL: binder = new QueryParameterAndBinder(SpecialAndListParam.class, @@ -106,7 +106,6 @@ public class ParameterUtil { throw new IllegalArgumentException("Parameter '" + theUnqualifiedParamName + "' has type " + paramType + " which is currently not supported."); } - // FIXME null access return binder.parse(theContext, theUnqualifiedParamName, theParameters); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java index 9d218105406..798da672753 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java @@ -76,24 +76,6 @@ public class ElementUtil { return true; } - /* - public static void validateAllElementsAreOfTypeOrThrowClassCastExceptionForModelSetter(List theList, Class theType) { - if (theList == null) { - return; - } - for (T next : theList) { - if (next != null && theType.isAssignableFrom(next.getClass()) == false) { - StringBuilder b = new StringBuilder(); - b.append("Failed to set invalid value, found element in list of type "); - b.append(next.getClass().getSimpleName()); - b.append(" but expected "); - b.append(theType.getName()); - throw new ClassCastException(b.toString()); - } - } - } - */ - public static boolean isEmpty(List theElements) { if (theElements == null) { return true; @@ -141,8 +123,7 @@ public class ElementUtil { //@SuppressWarnings("unchecked") private static void addElement(ArrayList retVal, IElement next, Class theType) { - //FIXME There seems to be an error on theType == null => if (theType != null|| theType.isAssignableFrom - if (theType == null|| theType.isAssignableFrom(next.getClass())) { + if (theType != null && theType.isAssignableFrom(next.getClass())) { retVal.add(theType.cast(next)); } if (next instanceof ICompositeElement) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index acc92dcb2a6..99d8ccd7939 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -58,7 +58,8 @@ public enum VersionEnum { V4_0_0, V4_0_3, V4_1_0, - V4_2_0; + V4_2_0, + V4_3_0; public static VersionEnum latestVersion() { VersionEnum[] values = VersionEnum.values(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java index f72a5bff66a..e2fe1cb2e97 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java @@ -39,12 +39,17 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; public class SchemaBaseValidator implements IValidatorModule { public static final String RESOURCES_JAR_NOTE = "Note that as of HAPI FHIR 1.2, DSTU2 validation files are kept in a separate JAR (hapi-fhir-validation-resources-XXX.jar) which must be added to your classpath. See the HAPI FHIR download page for more information."; @@ -179,18 +184,20 @@ public class SchemaBaseValidator implements IValidatorModule { input.setPublicId(thePublicId); input.setSystemId(theSystemId); input.setBaseURI(theBaseURI); - // String pathToBase = "ca/uhn/fhir/model/" + myVersion + "/schema/" + theSystemId; String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theSystemId; ourLog.debug("Loading referenced schema file: " + pathToBase); - InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); - if (baseIs == null) { - throw new InternalErrorException("Schema file not found: " + pathToBase); - } + try (InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase)) { + if (baseIs == null) { + throw new InternalErrorException("Schema file not found: " + pathToBase); + } - input.setByteStream(baseIs); - //FIXME resource leak + byte[] bytes = IOUtils.toByteArray(baseIs); + input.setByteStream(new ByteArrayInputStream(bytes)); + } catch (IOException e) { + throw new InternalErrorException(e); + } return input; } diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 7911327e69d..450e56c6aa0 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 53f125ac491..6d56d743525 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java index 2a92f8df72a..1fc92fc6595 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExportConceptMapToCsvCommand.java @@ -26,7 +26,6 @@ import org.apache.commons.cli.Options; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.QuoteMode; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.ConceptMap; @@ -43,6 +42,7 @@ import java.util.List; import java.util.concurrent.ExecutionException; import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap; public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConceptMapCommand { // TODO: Don't use qualified names for loggers in HAPI CLI. @@ -114,7 +114,7 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept private void convertConceptMapToCsv(org.hl7.fhir.dstu3.model.ConceptMap theConceptMap) throws ExecutionException { try { - convertConceptMapToCsv(VersionConvertor_30_40.convertConceptMap(theConceptMap)); + convertConceptMapToCsv(convertConceptMap(theConceptMap)); } catch (FHIRException fe) { throw new ExecutionException(fe); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java index d44570b5d91..a795af5510a 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ImportCsvToConceptMapCommand.java @@ -28,7 +28,6 @@ import org.apache.commons.cli.Options; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; @@ -45,7 +44,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap; public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConceptMapCommand { // TODO: Don't use qualified names for loggers in HAPI CLI. @@ -154,7 +156,7 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept private org.hl7.fhir.dstu3.model.ConceptMap convertCsvToConceptMapDstu3() throws ExecutionException { try { - return VersionConvertor_30_40.convertConceptMap(convertCsvToConceptMapR4()); + return convertConceptMap(convertCsvToConceptMapR4()); } catch (FHIRException fe) { throw new ExecutionException(fe); } @@ -174,7 +176,7 @@ public class ImportCsvToConceptMapCommand extends AbstractImportExportCsvConcept .withFirstRecordAsHeader() .withIgnoreHeaderCase() .withIgnoreEmptyLines() - .withTrim()); + .withTrim()) ) { retVal.setUrl(conceptMapUrl); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/UploadTerminologyCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/UploadTerminologyCommandTest.java index 0cf9e2b5069..2ed94ab0b9b 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/UploadTerminologyCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/UploadTerminologyCommandTest.java @@ -118,7 +118,7 @@ public class UploadTerminologyCommandTest extends BaseTest { assertEquals(1, listOfDescriptors.size()); assertEquals("concepts.csv", listOfDescriptors.get(0).getFilename()); String uploadFile = IOUtils.toString(listOfDescriptors.get(0).getInputStream(), Charsets.UTF_8); - assertThat(uploadFile, containsString("CODE,Display")); + assertThat(uploadFile, uploadFile, containsString("\"CODE\",\"Display\"")); } @Test diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index c1d6985409a..e8b4258a8e5 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 45108f849f7..649f92bc336 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 792990a296b..c3152ce67f4 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 615e60bbac8..1d01a8cb58e 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 69e09a3a601..f085e293764 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java index 6ba1988648f..5e5030c8a71 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java @@ -85,13 +85,11 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { public HttpClient getNativeHttpClient() { if (myHttpClient == null) { - //FIXME potential resoource leak PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); connectionManager.setMaxTotal(getPoolMaxTotal()); connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute()); - // @formatter:off //TODO: Use of a deprecated method should be resolved. RequestConfig defaultRequestConfig = RequestConfig.custom() @@ -114,7 +112,6 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { } myHttpClient = builder.build(); - // @formatter:on } @@ -128,7 +125,7 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { /** * Only allows to set an instance of type org.apache.http.client.HttpClient - * @see ca.uhn.fhir.rest.client.api.IRestfulClientFactory#setHttpClient(ca.uhn.fhir.rest.client.api.IHttpClient) + * @see ca.uhn.fhir.rest.client.api.IRestfulClientFactory#setHttpClient(Object) */ @Override public synchronized void setHttpClient(Object theHttpClient) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index 12e8d4b87cf..40804918554 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -1796,11 +1796,8 @@ public class GenericClient extends BaseClient implements IGenericClient { if (rootSs == null) { rootSs = nextSortSpec; } else { - // FIXME lastSs is null never set - // TODO unused assignment lastSs.setChain(nextSortSpec); } - // TODO unused assignment lastSs = nextSortSpec; } if (rootSs != null) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseQueryParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseQueryParameter.java index a1368c8c7eb..293025f3df2 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseQueryParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseQueryParameter.java @@ -1,5 +1,18 @@ package ca.uhn.fhir.rest.client.method; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -22,20 +35,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * #L% */ -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.hl7.fhir.instance.model.api.IBaseResource; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.api.QualifiedParamList; -import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; - public abstract class BaseQueryParameter implements IParameter { public abstract List encode(FhirContext theContext, Object theObject) throws InternalErrorException; @@ -44,12 +43,6 @@ public abstract class BaseQueryParameter implements IParameter { public abstract RestSearchParameterTypeEnum getParamType(); - /** - * Parameter should return true if {@link #parse(FhirContext, List)} should be called even if the query string - * contained no values for the given parameter - */ - public abstract boolean handlesMissing(); - @Override public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { // ignore for now @@ -57,8 +50,6 @@ public abstract class BaseQueryParameter implements IParameter { public abstract boolean isRequired(); - public abstract Object parse(FhirContext theContext, List theString) throws InternalErrorException, InvalidRequestException; - @Override public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException { if (theSourceClientArgument == null) { @@ -79,11 +70,7 @@ public abstract class BaseQueryParameter implements IParameter { String qualifier = nextParamEntry.getQualifier(); String paramName = isNotBlank(qualifier) ? getName() + qualifier : getName(); - List paramValues = theTargetQueryArguments.get(paramName); - if (paramValues == null) { - paramValues = new ArrayList<>(value.size()); - theTargetQueryArguments.put(paramName, paramValues); - } + List paramValues = theTargetQueryArguments.computeIfAbsent(paramName, k -> new ArrayList<>(value.size())); paramValues.add(b.toString()); } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/IncludeParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/IncludeParameter.java index 6631c989d3a..3326cc57558 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/IncludeParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/IncludeParameter.java @@ -19,16 +19,23 @@ package ca.uhn.fhir.rest.client.method; * limitations under the License. * #L% */ -import java.util.*; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; class IncludeParameter extends BaseQueryParameter { @@ -37,11 +44,12 @@ class IncludeParameter extends BaseQueryParameter { private Class mySpecType; private boolean myReverse; + public IncludeParameter(IncludeParam theAnnotation, Class> theInstantiableCollectionType, Class theSpecType) { myInstantiableCollectionType = theInstantiableCollectionType; myReverse = theAnnotation.reverse(); if (theAnnotation.allow().length > 0) { - myAllow = new HashSet(); + myAllow = new HashSet<>(); for (String next : theAnnotation.allow()) { if (next != null) { myAllow.add(next); @@ -61,7 +69,7 @@ class IncludeParameter extends BaseQueryParameter { @SuppressWarnings("unchecked") @Override public List encode(FhirContext theContext, Object theObject) throws InternalErrorException { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); if (myInstantiableCollectionType == null) { if (mySpecType == Include.class) { @@ -105,58 +113,9 @@ class IncludeParameter extends BaseQueryParameter { return null; } - @Override - public boolean handlesMissing() { - return true; - } - @Override public boolean isRequired() { return false; } - @Override - public Object parse(FhirContext theContext, List theString) throws InternalErrorException, InvalidRequestException { - Collection retValCollection = null; - - if (myInstantiableCollectionType != null) { - try { - retValCollection = myInstantiableCollectionType.newInstance(); - } catch (Exception e) { - throw new InternalErrorException("Failed to instantiate " + myInstantiableCollectionType.getName(), e); - } - } - - for (QualifiedParamList nextParamList : theString) { - if (nextParamList.isEmpty()) { - continue; - } - if (nextParamList.size() > 1) { - throw new InvalidRequestException(theContext.getLocalizer().getMessage(IncludeParameter.class, "orIncludeInRequest")); - } - - boolean recurse = Constants.PARAM_INCLUDE_QUALIFIER_RECURSE.equals(nextParamList.getQualifier()); - - String value = nextParamList.get(0); - if (myAllow != null && !myAllow.isEmpty()) { - if (!myAllow.contains(value)) { - if (!myAllow.contains("*")) { - String msg = theContext.getLocalizer().getMessage(IncludeParameter.class, "invalidIncludeNameInRequest", value, new TreeSet(myAllow).toString(), getName()); - throw new InvalidRequestException(msg); - } - } - } - if (myInstantiableCollectionType == null) { - if (mySpecType == String.class) { - return value; - } - return new Include(value, recurse); - } - //FIXME null access - retValCollection.add(new Include(value, recurse)); - } - - return retValCollection; - } - } diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java index 52774ecdbe8..5042bd0e810 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java @@ -1,29 +1,51 @@ package ca.uhn.fhir.rest.client.method; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.*; -import java.util.Map.Entry; - -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.*; - -import ca.uhn.fhir.context.*; -import ca.uhn.fhir.model.api.*; -import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PatchTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.method.OperationParameter.IOperationParamConverter; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.binder.CollectionBinder; -import ca.uhn.fhir.util.*; +import ca.uhn.fhir.util.DateUtils; +import ca.uhn.fhir.util.ParametersUtil; +import ca.uhn.fhir.util.ReflectionUtil; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IAnyResource; +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 java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; /* * #%L @@ -45,7 +67,6 @@ import ca.uhn.fhir.util.*; * #L% */ -@SuppressWarnings("deprecation") public class MethodUtil { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class); @@ -288,33 +309,25 @@ public class MethodUtil { specType = parameterType; } - param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, - specType); + param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, specType); } else if (nextAnnotation instanceof ResourceParam) { if (IBaseResource.class.isAssignableFrom(parameterType)) { // good } else if (String.class.equals(parameterType)) { // good - } else if (byte[].class.equals(parameterType)) { - // good - } else if (EncodingEnum.class.equals(parameterType)) { - // good } else { StringBuilder b = new StringBuilder(); b.append("Method '"); b.append(theMethod.getName()); b.append("' is annotated with @"); b.append(ResourceParam.class.getSimpleName()); - b.append(" but has a type that is not an implemtation of "); + b.append(" but has a type that is not an implementation of "); b.append(IBaseResource.class.getCanonicalName()); - b.append(" or String or byte[]"); throw new ConfigurationException(b.toString()); } param = new ResourceParameter(parameterType); } else if (nextAnnotation instanceof IdParam) { param = new NullParameter(); - } else if (nextAnnotation instanceof ServerBase) { - param = new ServerBaseParamBinder(); } else if (nextAnnotation instanceof Elements) { param = new ElementsParameter(); } else if (nextAnnotation instanceof Since) { @@ -345,19 +358,6 @@ public class MethodUtil { } param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_MODE, 0, 1).setConverter(new IOperationParamConverter() { - @Override - public Object incomingServer(Object theObject) { - if (isNotBlank(theObject.toString())) { - ValidationModeEnum retVal = ValidationModeEnum - .forCode(theObject.toString()); - if (retVal == null) { - OperationParameter.throwInvalidMode(theObject.toString()); - } - return retVal; - } - return null; - } - @Override public Object outgoingClient(Object theObject) { return ParametersUtil.createString(theContext, @@ -372,10 +372,6 @@ public class MethodUtil { } param = new OperationParameter(theContext, Constants.EXTOP_VALIDATE, Constants.EXTOP_VALIDATE_PROFILE, 0, 1).setConverter(new IOperationParamConverter() { - @Override - public Object incomingServer(Object theObject) { - return theObject.toString(); - } @Override public Object outgoingClient(Object theObject) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationParameter.java index f6615e838d6..a56b01acc46 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationParameter.java @@ -191,8 +191,6 @@ public class OperationParameter implements IParameter { interface IOperationParamConverter { - Object incomingServer(Object theObject); - Object outgoingClient(Object theObject); } @@ -203,13 +201,6 @@ public class OperationParameter implements IParameter { Validate.isTrue(mySearchParameterBinding != null); } - @Override - public Object incomingServer(Object theObject) { - IPrimitiveType obj = (IPrimitiveType) theObject; - List paramList = Collections.singletonList(QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null, obj.getValueAsString())); - return mySearchParameterBinding.parse(myContext, paramList); - } - @Override public Object outgoingClient(Object theObject) { IQueryParameterType obj = (IQueryParameterType) theObject; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java index 7d88cdf5167..8c9d1dfbc3c 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java @@ -156,26 +156,11 @@ public class SearchParameter extends BaseQueryParameter { return myType; } - @Override - public boolean handlesMissing() { - return false; - } - @Override public boolean isRequired() { return myRequired; } - /* - * (non-Javadoc) - * - * @see ca.uhn.fhir.rest.param.IParameter#parse(java.util.List) - */ - @Override - public Object parse(FhirContext theContext, List theString) throws InternalErrorException, InvalidRequestException { - return myParamBinder.parse(theContext, getName(), theString); - } - public void setChainlists(String[] theChainWhitelist) { myQualifierWhitelist = new HashSet<>(theChainWhitelist.length); myQualifierWhitelist.add(QUALIFIER_ANY_TYPE); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ServerBaseParamBinder.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ServerBaseParamBinder.java deleted file mode 100644 index a584a7d1cee..00000000000 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ServerBaseParamBinder.java +++ /dev/null @@ -1,47 +0,0 @@ -package ca.uhn.fhir.rest.client.method; - -/* - * #%L - * HAPI FHIR - Client Framework - * %% - * Copyright (C) 2014 - 2020 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import java.lang.reflect.Method; -import java.util.*; - -import org.hl7.fhir.instance.model.api.IBaseResource; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; - -class ServerBaseParamBinder implements IParameter { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerBaseParamBinder.class); - - @Override - public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException { - /* - * Does nothing, since we just ignore serverbase arguments - */ - ourLog.trace("Ignoring server base argument: {}", theSourceClientArgument); - } - - @Override - public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { - // ignore for now - } - -} diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index cfebc8a9f15..c346272f57d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java index 28df8b376a1..7e2bb24d80c 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java @@ -31,7 +31,11 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import org.hl7.fhir.converter.NullVersionConverterAdvisor30; import org.hl7.fhir.converter.NullVersionConverterAdvisor40; -import org.hl7.fhir.convertors.*; +import org.hl7.fhir.convertors.VersionConvertorAdvisor30; +import org.hl7.fhir.convertors.VersionConvertorAdvisor40; +import org.hl7.fhir.convertors.VersionConvertor_10_30; +import org.hl7.fhir.convertors.VersionConvertor_10_40; +import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -40,7 +44,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.StringTokenizer; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * This is an experimental interceptor! Use with caution as @@ -54,16 +60,12 @@ import static org.apache.commons.lang3.StringUtils.*; public class VersionedApiConverterInterceptor extends InterceptorAdapter { private final FhirContext myCtxDstu2; private final FhirContext myCtxDstu2Hl7Org; - private VersionConvertor_30_40 myVersionConvertor_30_40; - private VersionConvertor_10_40 myVersionConvertor_10_40; - private VersionConvertor_10_30 myVersionConvertor_10_30; + private final NullVersionConverterAdvisor40 advisor40; + private final NullVersionConverterAdvisor30 advisor30; public VersionedApiConverterInterceptor() { - myVersionConvertor_30_40 = new VersionConvertor_30_40(); - VersionConvertorAdvisor40 advisor40 = new NullVersionConverterAdvisor40(); - myVersionConvertor_10_40 = new VersionConvertor_10_40(advisor40); - VersionConvertorAdvisor30 advisor30 = new NullVersionConverterAdvisor30(); - myVersionConvertor_10_30 = new VersionConvertor_10_30(advisor30); + advisor40 = new NullVersionConverterAdvisor40(); + advisor30 = new NullVersionConverterAdvisor30(); myCtxDstu2 = FhirContext.forDstu2(); myCtxDstu2Hl7Org = FhirContext.forDstu2Hl7Org(); @@ -104,17 +106,17 @@ public class VersionedApiConverterInterceptor extends InterceptorAdapter { IBaseResource converted = null; try { if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) { - converted = myVersionConvertor_30_40.convertResource(toDstu3(responseResource), true); + converted = VersionConvertor_30_40.convertResource(toDstu3(responseResource), true); } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) { - converted = myVersionConvertor_30_40.convertResource(toR4(responseResource), true); + converted = VersionConvertor_30_40.convertResource(toR4(responseResource), true); } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) { - converted = myVersionConvertor_10_40.convertResource(toR4(responseResource)); + converted = VersionConvertor_10_40.convertResource(toR4(responseResource), advisor40); } else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) { - converted = myVersionConvertor_10_40.convertResource(toDstu2(responseResource)); + converted = VersionConvertor_10_40.convertResource(toDstu2(responseResource), advisor40); } else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.DSTU3) { - converted = myVersionConvertor_10_30.convertResource(toDstu3(responseResource)); + converted = VersionConvertor_10_30.convertResource(toDstu3(responseResource), advisor30); } else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.DSTU2) { - converted = myVersionConvertor_10_30.convertResource(toDstu2(responseResource)); + converted = VersionConvertor_10_30.convertResource(toDstu2(responseResource), advisor30); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java index 8b00abdffad..33d703f7a88 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor50.java @@ -27,8 +27,12 @@ import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.ValueSet; +import java.util.IdentityHashMap; + public class NullVersionConverterAdvisor50 implements VersionConvertorAdvisor50 { + private IdentityHashMap myCodeSystems = new IdentityHashMap<>(); + @Override public boolean ignoreEntry(Bundle.BundleEntryComponent src) { return false; @@ -56,11 +60,11 @@ public class NullVersionConverterAdvisor50 implements VersionConvertorAdvisor50 @Override public void handleCodeSystem(CodeSystem tgtcs, ValueSet source) throws FHIRException { - + myCodeSystems.put(source, tgtcs); } @Override public CodeSystem getCodeSystem(ValueSet src) throws FHIRException { - return null; + return myCodeSystems.get(src); } } diff --git a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java index be77cdb8515..a63a0d86344 100644 --- a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java +++ b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_10_30Test.java @@ -16,13 +16,10 @@ public class VersionConvertor_10_30Test { @Test public void testConvert() throws FHIRException { - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = (Observation) VersionConvertor_10_30.convertResource(input); String context = output.getContext().getReference(); assertEquals("Encounter/123", context); @@ -31,9 +28,6 @@ public class VersionConvertor_10_30Test { @Test public void testConvertSpecimen() throws FHIRException { - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - Specimen spec = new Specimen(); CodeableConcept cc = new CodeableConcept(); Coding coding = new Coding(); @@ -58,7 +52,7 @@ public class VersionConvertor_10_30Test { Specimen.SpecimenContainerComponent specimenContainerComponent = new Specimen.SpecimenContainerComponent(); specimenContainerComponent.getExtension().add(new Extension().setUrl("some_url").setValue(new StringType("some_value"))); spec.setContainer(Collections.singletonList(specimenContainerComponent)); - Resource resource = converter.convertResource(spec); + Resource resource = VersionConvertor_10_30.convertResource(spec); } diff --git a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java index 14fa9db116a..8914d474e01 100644 --- a/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java +++ b/hapi-fhir-converter/src/test/java/org/hl7/fhir/converter/VersionConvertor_14_30Test.java @@ -3,6 +3,7 @@ package org.hl7.fhir.converter; import static org.junit.Assert.assertEquals; import org.hl7.fhir.convertors.VersionConvertor_14_30; +import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.exceptions.FHIRException; import org.junit.Test; @@ -14,7 +15,7 @@ public class VersionConvertor_14_30Test { org.hl7.fhir.dstu2016may.model.Questionnaire input = new org.hl7.fhir.dstu2016may.model.Questionnaire(); input.setTitle("My title"); - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = (Questionnaire) VersionConvertor_14_30.convertResource(input); String context = output.getTitle(); assertEquals("My title", context); diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 04bb687721d..ec6e0a219ca 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 83a317dfd90..31d5fdd86e2 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -73,13 +73,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT compile ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT compile @@ -96,7 +96,7 @@ ca.uhn.hapi.fhir hapi-fhir-testpage-overlay - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT classes diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java index 5da62480bdc..0da2042b969 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConverterExamples.java @@ -20,9 +20,8 @@ package ca.uhn.hapi.fhir.docs; * #L% */ -import org.hl7.fhir.converter.NullVersionConverterAdvisor30; -import org.hl7.fhir.convertors.VersionConvertor_10_30; -import org.hl7.fhir.convertors.VersionConvertor_14_30; +import org.hl7.fhir.convertors.conv10_30.Observation10_30; +import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30; import org.hl7.fhir.exceptions.FHIRException; public class ConverterExamples { @@ -30,16 +29,12 @@ public class ConverterExamples { @SuppressWarnings("unused") public void c1020() throws FHIRException { //START SNIPPET: 1020 - // Create a converter - NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30(); - VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor); - // Create an input resource to convert org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation(); input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123")); // Convert the resource - org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input); + org.hl7.fhir.dstu3.model.Observation output = Observation10_30.convertObservation(input); String context = output.getContext().getReference(); //END SNIPPET: 1020 } @@ -52,7 +47,7 @@ public class ConverterExamples { input.setTitle("My title"); // Convert the resource - org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input); + org.hl7.fhir.dstu3.model.Questionnaire output = Questionnaire14_30.convertQuestionnaire(input); String context = output.getTitle(); //END SNIPPET: 1420 } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json index 79a667fbb5c..c33856d2b24 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json @@ -225,17 +225,6 @@ "lon": -3.6840, "added": "2019-09-29" }, - { - "title": "ResMed", - "description": "Digital Health Technologies: The largest tele-monitored device fleet in the world. Focus on various respiratory diseases from Sleep Disordered breathing (sleep apnea) to COPD.", - "link": null, - "contactName": null, - "contactEmail": null, - "city": "San Diego, California", - "lat": 32.8246, - "lon": -117.1971, - "added": "2019-09-29" - }, { "title": "Highmark", "description": "FHIR-based Provider Directory Message Model: Using R4 FHIR we created a standard provider directory message model that captures all practitioners, practitioner roles, vendors, vendor roles, locations, healthcare services, affiliated directory networks and tiered insurance plans. We supply this FHIR-based extract to a well-known transparency vendor whom processes our FHIR message model extract and loads the data to be consumed and accessed on a web-based provider search application. Members can then access this provider search app to search for practitioners whom participate in their products and networks.", diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/1692-resource-methods-on-interfaces.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/1692-resource-methods-on-interfaces.yaml new file mode 100644 index 00000000000..de3bcf45597 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/1692-resource-methods-on-interfaces.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 1694 +title: It is now possible to specify resource provider method annotations on interface methods implemented by + resource provider classes, as opposed to needing to specify them directly on the concrete class. Thanks + to Tue Toft Nørgård for the pull request! diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml index a01f7a1402d..f4ba414d4ac 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_2_0/version.yaml @@ -1,2 +1,3 @@ --- -release-date: "TBD" +release-date: "2020-02-15" +codename: "Koala" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml new file mode 100644 index 00000000000..874c6fe3520 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1715-update-converters.yaml @@ -0,0 +1,6 @@ +--- +type: change +issue: 1715 +title: The version converters for all versions except R4/R5 have been reworked to be split into individual + classes per resource type (the R4/R5 converters were already organized this way). Thanks to Mark Iantorno + for a huge effort to write a Java source parser/serializer to acomplish this task. diff --git a/hapi-fhir-igpacks/pom.xml b/hapi-fhir-igpacks/pom.xml index 4e369e28cab..0a0fba57081 100644 --- a/hapi-fhir-igpacks/pom.xml +++ b/hapi-fhir-igpacks/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 3cd53b5143d..34d8a852672 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 47d8f3872fb..d8d18016d9e 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java index 38a9436dba5..74d28d74aa1 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jaxrs.server.util; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.server.ParseAction; +import ca.uhn.fhir.rest.server.RestfulResponse; +import ca.uhn.fhir.rest.server.RestfulServerUtils; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseBinary; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.List; +import java.util.Map.Entry; + import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -21,23 +41,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * limitations under the License. * #L% */ -import java.io.*; -import java.util.List; -import java.util.Map.Entry; - -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.IBaseBinary; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.ParseAction; -import ca.uhn.fhir.rest.server.RestfulResponse; -import ca.uhn.fhir.rest.server.RestfulServerUtils; /** * The JaxRsResponse is a jax-rs specific implementation of the RestfulResponse. @@ -60,8 +63,7 @@ public class JaxRsResponse extends RestfulResponse { * by the server. */ @Override - public Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) - throws UnsupportedEncodingException, IOException { + public Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) { return new StringWriter(); } @@ -78,7 +80,7 @@ public class JaxRsResponse extends RestfulResponse { } @Override - public Response sendAttachmentResponse(IBaseBinary bin, int statusCode, String contentType) throws IOException { + public Object sendAttachmentResponse(IBaseBinary bin, int statusCode, String contentType) { ResponseBuilder response = buildResponse(statusCode); if (bin.getContent() != null && bin.getContent().length > 0) { response.header(Constants.HEADER_CONTENT_TYPE, contentType).entity(bin.getContent()); diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index 2bb47ad450c..1f342519db5 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java b/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java index 9c3db5e1244..1dbf355bf6b 100644 --- a/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-example/src/test/java/ca/uhn/fhir/jaxrs/server/example/JaxRsPatientProviderDstu3Test.java @@ -199,26 +199,7 @@ public class JaxRsPatientProviderDstu3Test { //assertEquals(e.getStatusCode(), Constants.STATUS_HTTP_404_NOT_FOUND); } } - - /** Transaction - Server */ - @Ignore - @Test - public void testTransaction() { - Bundle bundle = new Bundle(); - BundleEntryComponent entry = bundle.addEntry(); - final Patient existing = new Patient(); - existing.getName().get(0).setFamily("Created with bundle"); - entry.setResource(existing); - // FIXME ? -// BoundCodeDt theTransactionOperation = -// new BoundCodeDt( -// BundleEntryTransactionMethodEnum.VALUESET_BINDER, -// BundleEntryTransactionMethodEnum.POST); -// entry.setTransactionMethod(theTransactionOperation); - Bundle response = client.transaction().withBundle(bundle).execute(); - } - /** Conformance - Server */ @Test @Ignore diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 8fbfd4e4aca..00fd60b7ff5 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -608,7 +608,7 @@ compile - + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 785ea06a62a..fa4b228bb78 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -368,7 +368,6 @@ public abstract class BaseHapiFhirDao extends BaseStora Search search = new Search(); search.setDeleted(false); search.setCreated(new Date()); - search.setSearchLastReturned(new Date()); search.setLastUpdated(theSince, theUntil); search.setUuid(UUID.randomUUID().toString()); search.setResourceType(resourceName); 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 8a5bfe6f555..804ecd56017 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 @@ -1097,11 +1097,10 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public Set searchForIds(SearchParameterMap theParams, RequestDetails theRequest) { + theParams.setLoadSynchronousUpTo(10000); ISearchBuilder builder = mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); - // FIXME: fail if too many results - HashSet retVal = new HashSet<>(); String uuid = UUID.randomUUID().toString(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoStructureDefinitionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoStructureDefinitionDstu2.java index e4ded4f5078..bf0f1e42476 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoStructureDefinitionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoStructureDefinitionDstu2.java @@ -21,12 +21,12 @@ package ca.uhn.fhir.jpa.dao; */ import ca.uhn.fhir.model.dstu2.resource.StructureDefinition; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class FhirResourceDaoStructureDefinitionDstu2 extends BaseHapiFhirResourceDao implements IFhirResourceDaoStructureDefinition { @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) { - // FIXME: implement - return null; + throw new InvalidRequestException("Snapshot generation not supported for DSTU2"); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java index ce4795e9630..1b84f581849 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IResultIterator.java @@ -29,4 +29,6 @@ public interface IResultIterator extends Iterator, Closeab int getSkippedCount(); + int getNonSkippedCount(); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 3ba2c792a74..99b349c698d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -65,6 +65,7 @@ import org.apache.commons.lang3.Validate; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.query.Query; +import org.hl7.fhir.dstu3.model.Location; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -160,6 +161,10 @@ public class SearchBuilder implements ISearchBuilder { // Remove any empty parameters theParams.clean(); + if (myResourceType == Location.class) { + theParams.setLocationDistance(); + } + /* * Check if there is a unique key associated with the set * of parameters passed in @@ -181,7 +186,6 @@ public class SearchBuilder implements ISearchBuilder { } - @Override public Iterator createCountQuery(SearchParameterMap theParams, String theSearchUuid, RequestDetails theRequest) { init(theParams, theSearchUuid); @@ -250,7 +254,8 @@ public class SearchBuilder implements ISearchBuilder { outerQuery.multiselect(myBuilder.countDistinct(myQueryRoot.getRoot())); } else { outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class)); - outerQuery.distinct(true); + // KHS This distinct call is causing performance issues in large installations +// outerQuery.distinct(true); } } @@ -961,6 +966,7 @@ public class SearchBuilder implements ISearchBuilder { private SortSpec mySort; private boolean myStillNeedToFetchIncludes; private int mySkipCount = 0; + private int myNonSkipCount = 0; private QueryIterator(SearchRuntimeDetails theSearchRuntimeDetails, RequestDetails theRequest) { mySearchRuntimeDetails = theSearchRuntimeDetails; @@ -990,14 +996,7 @@ public class SearchBuilder implements ISearchBuilder { myMaxResultsToFetch = myDaoConfig.getFetchSizeDefaultMaximum(); } - final TypedQuery query = createQuery(mySort, myMaxResultsToFetch, false, myRequest); - - mySearchRuntimeDetails.setQueryStopwatch(new StopWatch()); - - Query hibernateQuery = (Query) query; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - myResultsIterator = new ScrollableResultsIterator<>(scroll); + initializeIteratorQuery(myMaxResultsToFetch); // If the query resulted in extra results being requested if (myAlsoIncludePids != null) { @@ -1032,11 +1031,32 @@ public class SearchBuilder implements ISearchBuilder { ResourcePersistentId next = new ResourcePersistentId(nextLong); if (myPidSet.add(next)) { myNext = next; + myNonSkipCount++; break; } else { mySkipCount++; } } + + if (!myResultsIterator.hasNext()) { + if (myMaxResultsToFetch != null && (mySkipCount + myNonSkipCount == myMaxResultsToFetch)) { + if (mySkipCount > 0 && myNonSkipCount == 0) { + myMaxResultsToFetch += 1000; + + StorageProcessingMessage message = new StorageProcessingMessage(); + String msg = "Pass completed with no matching results. This indicates an inefficient query! Retrying with new max count of " + myMaxResultsToFetch; + ourLog.warn(msg); + message.setMessage(msg); + HookParams params = new HookParams() + .add(RequestDetails.class, myRequest) + .addIfMatchesType(ServletRequestDetails.class, myRequest) + .add(StorageProcessingMessage.class, message); + JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params); + + initializeIteratorQuery(myMaxResultsToFetch); + } + } + } } } @@ -1096,6 +1116,20 @@ public class SearchBuilder implements ISearchBuilder { } + private void initializeIteratorQuery(Integer theMaxResultsToFetch) { + final TypedQuery query = createQuery(mySort, theMaxResultsToFetch, false, myRequest); + + mySearchRuntimeDetails.setQueryStopwatch(new StopWatch()); + + Query hibernateQuery = (Query) query; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + myResultsIterator = new ScrollableResultsIterator<>(scroll); + + mySkipCount = 0; + myNonSkipCount = 0; + } + @Override public boolean hasNext() { if (myNext == null) { @@ -1118,6 +1152,11 @@ public class SearchBuilder implements ISearchBuilder { return mySkipCount; } + @Override + public int getNonSkippedCount() { + return myNonSkipCount; + } + @Override public void close() { if (myResultsIterator != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java index 7e775a4bb6c..65738c08c77 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.jpa.model.entity.ForcedId; public interface IForcedIdDao extends JpaRepository { - // FIXME: JA We should log a performance warning if this is used since it's not indexed @Query("SELECT f.myResourcePid FROM ForcedId f WHERE myForcedId IN (:forced_id)") List findByForcedId(@Param("forced_id") Collection theForcedId); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java index 47c7d92cdf7..cf1dbc4e7a8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -38,16 +37,12 @@ public interface ISearchDao extends JpaRepository { @Query("SELECT s FROM Search s LEFT OUTER JOIN FETCH s.myIncludes WHERE s.myUuid = :uuid") Optional findByUuidAndFetchIncludes(@Param("uuid") String theUuid); - @Query("SELECT s.myId FROM Search s WHERE (s.mySearchLastReturned < :cutoff) AND (s.myExpiryOrNull IS NULL OR s.myExpiryOrNull < :now)") - Slice findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow, Pageable thePage); + @Query("SELECT s.myId FROM Search s WHERE (s.myCreated < :cutoff) AND (s.myExpiryOrNull IS NULL OR s.myExpiryOrNull < :now)") + Slice findWhereCreatedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow, Pageable thePage); @Query("SELECT s FROM Search s WHERE s.myResourceType = :type AND mySearchQueryStringHash = :hash AND (s.myCreated > :cutoff) AND s.myDeleted = false AND s.myStatus <> 'FAILED'") Collection findWithCutoffOrExpiry(@Param("type") String theResourceType, @Param("hash") int theHashCode, @Param("cutoff") Date theCreatedCutoff); - @Modifying - @Query("UPDATE Search s SET s.mySearchLastReturned = :last WHERE s.myId = :pid") - void updateSearchLastReturned(@Param("pid") long thePid, @Param("last") Date theDate); - @Modifying @Query("UPDATE Search s SET s.myDeleted = :deleted WHERE s.myId = :pid") void updateDeleted(@Param("pid") Long thePid, @Param("deleted") boolean theDeleted); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetConceptDesignationDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetConceptDesignationDao.java index cc84b88412a..49a1f0367da 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetConceptDesignationDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetConceptDesignationDao.java @@ -21,8 +21,6 @@ package ca.uhn.fhir.jpa.dao.data; */ import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -37,6 +35,4 @@ public interface ITermValueSetConceptDesignationDao extends JpaRepository findByTermValueSetConceptId(Pageable thePage, @Param("pid") Long theValueSetConceptId); } 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 1377a5287cd..0947af92164 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 @@ -24,10 +24,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; +import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; @@ -35,7 +35,6 @@ import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeableConcept; @@ -53,6 +52,7 @@ import java.util.List; import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; @Transactional public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -147,7 +147,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoConceptMap { @Autowired private ITermReadSvc myHapiTerminologySvc; @@ -166,7 +171,7 @@ public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoValueSetDstu3.class); @@ -420,7 +423,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; - myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet)); + myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSet)); } else { myHapiTerminologySvc.deleteValueSetAndChildren(retVal); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java index 51aa90a45de..d0d904c3b3d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java @@ -13,7 +13,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.*; -import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.*; @@ -43,8 +42,8 @@ import static org.apache.commons.lang3.StringUtils.left; @Table(name = "HFJ_SEARCH", uniqueConstraints = { @UniqueConstraint(name = "IDX_SEARCH_UUID", columnNames = "SEARCH_UUID") }, indexes = { - @Index(name = "IDX_SEARCH_LASTRETURNED", columnList = "SEARCH_LAST_RETURNED"), - @Index(name = "IDX_SEARCH_RESTYPE_HASHS", columnList = "RESOURCE_TYPE,SEARCH_QUERY_STRING_HASH,CREATED") + @Index(name = "IDX_SEARCH_RESTYPE_HASHS", columnList = "RESOURCE_TYPE,SEARCH_QUERY_STRING_HASH,CREATED"), + @Index(name = "IDX_SEARCH_CREATED", columnList = "CREATED") }) public class Search implements ICachedSearchDetails, Serializable { @@ -90,11 +89,6 @@ public class Search implements ICachedSearchDetails, Serializable { private Long myResourceId; @Column(name = "RESOURCE_TYPE", length = 200, nullable = true) private String myResourceType; - @NotNull - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "SEARCH_LAST_RETURNED", nullable = false, updatable = false) - @OptimisticLock(excluded = true) - private Date mySearchLastReturned; @Lob() @Basic(fetch = FetchType.LAZY) @Column(name = "SEARCH_QUERY_STRING", nullable = true, updatable = false, length = MAX_SEARCH_QUERY_STRING) @@ -261,14 +255,6 @@ public class Search implements ICachedSearchDetails, Serializable { myResourceType = theResourceType; } - public Date getSearchLastReturned() { - return mySearchLastReturned; - } - - public void setSearchLastReturned(Date theDate) { - mySearchLastReturned = theDate; - } - public String getSearchQueryString() { return mySearchQueryString; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java index ea6582e49cf..bca66a3792b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java @@ -33,9 +33,7 @@ import java.io.Serializable; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; -@Table(name = "TRM_VALUESET_C_DESIGNATION", indexes = { - @Index(name = "IDX_VALUESET_C_DSGNTN_VAL", columnList = "VAL") -}) +@Table(name = "TRM_VALUESET_C_DESIGNATION") @Entity() public class TermValueSetConceptDesignation implements Serializable { private static final long serialVersionUID = 1L; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/JpaPreResourceAccessDetails.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/JpaPreResourceAccessDetails.java index 316614b34ec..77fa992ed6b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/JpaPreResourceAccessDetails.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/JpaPreResourceAccessDetails.java @@ -57,7 +57,6 @@ public class JpaPreResourceAccessDetails implements IPreResourceAccessDetails { public IBaseResource getResource(int theIndex) { if (myResources == null) { myResources = new ArrayList<>(myResourcePids.size()); - // FIXME: JA don't call interceptors for this query mySearchBuilderSupplier.call().loadResourcesByPid(myResourcePids, Collections.emptySet(), myResources, false, null); } return myResources.get(theIndex); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index 5682960d637..90a27e56c48 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -39,7 +39,6 @@ import ca.uhn.fhir.util.ValidateUtil; import com.google.common.base.Charsets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -53,9 +52,15 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.trim; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; public class TerminologyUploaderProvider extends BaseJpaProvider { @@ -226,9 +231,9 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { b.append(ConceptHandler.DISPLAY); b.append("\n"); for (Map.Entry nextEntry : codes.entrySet()) { - b.append(nextEntry.getKey()); + b.append(csvEscape(nextEntry.getKey())); b.append(","); - b.append(defaultString(nextEntry.getValue())); + b.append(csvEscape(nextEntry.getValue())); b.append("\n"); } byte[] bytes = b.toString().getBytes(Charsets.UTF_8); @@ -245,9 +250,9 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { b.append(HierarchyHandler.PARENT); b.append("\n"); for (Map.Entry nextEntry : codeToParentCodes.entries()) { - b.append(nextEntry.getKey()); + b.append(csvEscape(nextEntry.getKey())); b.append(","); - b.append(defaultString(nextEntry.getValue())); + b.append(csvEscape(nextEntry.getValue())); b.append("\n"); } byte[] bytes = b.toString().getBytes(Charsets.UTF_8); @@ -267,10 +272,10 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { CodeSystem nextCodeSystem; switch (getContext().getVersion().getVersion()) { case DSTU3: - nextCodeSystem = VersionConvertor_30_40.convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem); + nextCodeSystem = convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem); break; case R5: - nextCodeSystem = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem); + nextCodeSystem = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem); break; default: nextCodeSystem = (CodeSystem) theCodeSystem; @@ -354,7 +359,6 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { return retVal; } - private static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor { private final File myNextFile; @@ -376,4 +380,13 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { } } } + + private static String csvEscape(String theValue) { + return '"' + + theValue + .replace("\"", "\"\"") + .replace("\n", "\\n") + .replace("\r", "") + + '"'; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java index 7bcfee951a4..0f42082bbef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java @@ -21,9 +21,9 @@ package ca.uhn.fhir.jpa.provider.dstu3; */ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.term.TranslationRequest; import ca.uhn.fhir.jpa.term.TranslationResult; -import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -31,11 +31,21 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.ConceptMap; +import org.hl7.fhir.dstu3.model.IdType; +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.exceptions.FHIRException; import javax.servlet.http.HttpServletRequest; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; + public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderDstu3 { @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), @@ -129,7 +139,7 @@ public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderD TranslationResult result = dao.translate(translationRequest, theRequestDetails); // Convert from R4 to DSTU3 - return VersionConvertor_30_40.convertParameters(result.toParameters()); + return convertParameters(result.toParameters()); } catch (FHIRException fe) { throw new InternalErrorException(fe); } finally { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java index e6376d2ddf1..42aec253031 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaResourceProviderDstu3.java @@ -21,17 +21,28 @@ package ca.uhn.fhir.jpa.provider.dstu3; */ import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider; +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; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -41,6 +52,7 @@ import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; public class JpaResourceProviderDstu3 extends BaseJpaResourceProvider { @@ -91,7 +103,7 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); try { - return VersionConvertor_30_40.convertParameters(retVal); + return convertParameters(retVal); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -107,7 +119,7 @@ public class JpaResourceProviderDstu3 extends BaseJpaRes RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters retVal = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); try { - return VersionConvertor_30_40.convertParameters(retVal); + return convertParameters(retVal); } catch (FHIRException e) { throw new InternalErrorException(e); } 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 f345c6f7361..c1f1970a0c1 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 @@ -3,17 +3,26 @@ package ca.uhn.fhir.jpa.provider.dstu3; 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.BaseJpaSystemProviderDstu2Plus; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.DecimalType; +import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IIdType; @@ -29,6 +38,7 @@ import java.util.TreeMap; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.hl7.fhir.convertors.conv30_40.Parameters30_40.convertParameters; /* * #%L @@ -72,7 +82,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProviderDstu2Plus dao = (IFhirResourceDaoConceptMap) getDao(); TranslationResult result = dao.translate(translationRequest, theRequestDetails); org.hl7.fhir.r4.model.Parameters parameters = result.toParameters(); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java index e3e4c6b54de..ef86e4f478b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaResourceProviderR5.java @@ -86,7 +86,7 @@ public class JpaResourceProviderR5 extends BaseJpaResour RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } @@ -99,7 +99,7 @@ public class JpaResourceProviderR5 extends BaseJpaResour @OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions, RequestDetails theRequest) { org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest); - return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters); + return org.hl7.fhir.convertors.conv40_50.Parameters40_50.convertParameters(parameters); } @Operation(name = OPERATION_META, idempotent = true, returnParameters = { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java index 7bacb42b6ba..4a99a931bbe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/JpaSystemProviderR5.java @@ -68,7 +68,7 @@ public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus myUnsyncedLastUpdated = new ConcurrentHashMap<>(); - - @Override - public void updateSearchLastReturned(Search theSearch, Date theDate) { - myUnsyncedLastUpdated.put(theSearch.getId(), theDate); - } - - @PostConstruct - public void scheduleJob() { - ScheduledJobDefinition jobDetail = new ScheduledJobDefinition(); - jobDetail.setId(getClass().getName()); - jobDetail.setJobClass(Job.class); - mySchedulerService.scheduleLocalJob(10 * DateUtils.MILLIS_PER_SECOND, jobDetail); - } - - @Override - public void flushLastUpdated() { - TransactionTemplate txTemplate = new TransactionTemplate(myTxManager); - txTemplate.execute(t -> { - for (Iterator> iter = myUnsyncedLastUpdated.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry next = iter.next(); - flushLastUpdated(next.getKey(), next.getValue()); - iter.remove(); - } - return null; - }); - } - - protected abstract void flushLastUpdated(Long theSearchId, Date theLastUpdated); - - public static class Job implements HapiJob { - @Autowired - private ISearchCacheSvc myTarget; - - @Override - public void execute(JobExecutionContext theContext) { - myTarget.flushLastUpdated(); - } - } - - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java index 5a008d0f61c..8086644ab79 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java @@ -47,7 +47,7 @@ import java.util.Date; import java.util.List; import java.util.Optional; -public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { +public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { /* * Be careful increasing this number! We use the number of params here in a * DELETE FROM foo WHERE params IN (term,term,term...) @@ -55,7 +55,7 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { */ public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT = 500; public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 20000; - public static final long DEFAULT_CUTOFF_SLACK = 10 * DateUtils.MILLIS_PER_SECOND; + public static final long SEARCH_CLEANUP_JOB_INTERVAL_MILLIS = 10 * DateUtils.MILLIS_PER_SECOND; private static final Logger ourLog = LoggerFactory.getLogger(DatabaseSearchCacheSvcImpl.class); private static int ourMaximumResultsToDeleteInOneStatement = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT; private static int ourMaximumResultsToDeleteInOnePass = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS; @@ -65,7 +65,7 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { * is being reused (because a new client request came in with the same params) right before * the result is to be deleted */ - private long myCutoffSlack = DEFAULT_CUTOFF_SLACK; + private long myCutoffSlack = SEARCH_CLEANUP_JOB_INTERVAL_MILLIS; @Autowired private ISearchDao mySearchDao; @@ -146,11 +146,6 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { } - @Override - protected void flushLastUpdated(Long theSearchId, Date theLastUpdated) { - mySearchDao.updateSearchLastReturned(theSearchId, theLastUpdated); - } - @Transactional(Transactional.TxType.NEVER) @Override public void pollForStaleSearchesAndDeleteThem() { @@ -160,7 +155,7 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { long cutoffMillis = myDaoConfig.getExpireSearchResultsAfterMillis(); if (myDaoConfig.getReuseCachedSearchResultsForMillis() != null) { - cutoffMillis = Math.max(cutoffMillis, myDaoConfig.getReuseCachedSearchResultsForMillis()); + cutoffMillis = cutoffMillis + myDaoConfig.getReuseCachedSearchResultsForMillis(); } final Date cutoff = new Date((now() - cutoffMillis) - myCutoffSlack); @@ -172,7 +167,7 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { TransactionTemplate tt = new TransactionTemplate(myTxManager); final Slice toDelete = tt.execute(theStatus -> - mySearchDao.findWhereLastReturnedBefore(cutoff, new Date(), PageRequest.of(0, 2000)) + mySearchDao.findWhereCreatedBefore(cutoff, new Date(), PageRequest.of(0, 2000)) ); assert toDelete != null; @@ -223,7 +218,7 @@ public class DatabaseSearchCacheSvcImpl extends BaseSearchCacheSvcImpl { // Only delete if we don't have results left in this search if (resultPids.getNumberOfElements() < max) { - ourLog.debug("Deleting search {}/{} - Created[{}] -- Last returned[{}]", searchToDelete.getId(), searchToDelete.getUuid(), new InstantType(searchToDelete.getCreated()), new InstantType(searchToDelete.getSearchLastReturned())); + ourLog.debug("Deleting search {}/{} - Created[{}]", searchToDelete.getId(), searchToDelete.getUuid(), new InstantType(searchToDelete.getCreated())); mySearchDao.deleteByPid(searchToDelete.getId()); } else { ourLog.debug("Purged {} search results for deleted search {}/{}", resultPids.getSize(), searchToDelete.getId(), searchToDelete.getUuid()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java index 90d9b1124e6..82b35d526e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java @@ -78,20 +78,6 @@ public interface ISearchCacheSvc { */ Collection findCandidatesForReuse(String theResourceType, String theQueryString, int theQueryStringHash, Date theCreatedAfter); - /** - * Mark a search as having been "last used" at the given time. This method may (and probably should) be implemented - * to work asynchronously in order to avoid hammering the database if the search gets reused many times. - * - * @param theSearch The search - * @param theDate The "last returned" timestamp - */ - void updateSearchLastReturned(Search theSearch, Date theDate); - - /** - * This is mostly public for unit tests - */ - void flushLastUpdated(); - /** * This method will be called periodically to delete stale searches. Implementations are not required to do anything * if they have some other mechanism for expiring stale results other than manually looking for them diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionTriggeringSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionTriggeringSvcImpl.java index 32630819ce4..49506449095 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionTriggeringSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/SubscriptionTriggeringSvcImpl.java @@ -66,7 +66,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider.RESOURCE_ID; @@ -136,8 +142,8 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc SubscriptionTriggeringJobDetails jobDetails = new SubscriptionTriggeringJobDetails(); jobDetails.setJobId(UUID.randomUUID().toString()); - jobDetails.setRemainingResourceIds(resourceIds.stream().map(t->t.getValue()).collect(Collectors.toList())); - jobDetails.setRemainingSearchUrls(searchUrls.stream().map(t->t.getValue()).collect(Collectors.toList())); + jobDetails.setRemainingResourceIds(resourceIds.stream().map(t -> t.getValue()).collect(Collectors.toList())); + jobDetails.setRemainingSearchUrls(searchUrls.stream().map(t -> t.getValue()).collect(Collectors.toList())); if (theSubscriptionId != null) { jobDetails.setSubscriptionId(theSubscriptionId.getIdPart()); } @@ -386,7 +392,13 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc ScheduledJobDefinition jobDetail = new ScheduledJobDefinition(); jobDetail.setId(getClass().getName()); jobDetail.setJobClass(Job.class); - mySchedulerService.scheduleLocalJob(60 * DateUtils.MILLIS_PER_SECOND, jobDetail); + // Currently jobs ae kept in a local ArrayList so this should be a local job, and + // it can fire frequently without adding load + mySchedulerService.scheduleLocalJob(5 * DateUtils.MILLIS_PER_SECOND, jobDetail); + } + + public int getActiveJobCount() { + return myActiveJobs.size(); } public static class Job implements HapiJob { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index fe2a72fbd1f..02891e7c508 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1414,7 +1414,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo TermConceptMapGroupElement termConceptMapGroupElement; for (ConceptMap.SourceElementComponent element : group.getElement()) { if (isBlank(element.getCode())) { - // FIXME: JA - send this to an interceptor message so it can be output continue; } termConceptMapGroupElement = new TermConceptMapGroupElement(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 461442de503..f7b151d3fa0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -388,7 +388,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { } } - // FIXME: DM 2019-09-13 - Manually add EXTERNAL_COPYRIGHT_NOTICE property until Regenstrief adds this to loinc.xml + // TODO: DM 2019-09-13 - Manually add EXTERNAL_COPYRIGHT_NOTICE property until Regenstrief adds this to loinc.xml if (!propertyNamesToTypes.containsKey("EXTERNAL_COPYRIGHT_NOTICE")) { String externalCopyRightNoticeCode = "EXTERNAL_COPYRIGHT_NOTICE"; CodeSystem.PropertyType externalCopyRightNoticeType = CodeSystem.PropertyType.STRING; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java index 6f949a61933..aee82c3ce8c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java @@ -9,10 +9,13 @@ import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ValidateUtil; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; -import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.CodeSystem; 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.StructureDefinition; +import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.exceptions.FHIRException; @@ -31,6 +34,9 @@ import java.util.List; import java.util.Optional; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; +import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; +import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSetExpansionComponent; /* * #%L @@ -104,7 +110,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand); org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = super.expandValueSetInMemory(valueSetToExpandR4, null).getExpansion(); - return VersionConvertor_30_40.convertValueSetExpansionComponent(expandedR4); + return convertValueSetExpansionComponent(expandedR4); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -118,7 +124,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand); org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSetInMemory(valueSetToExpandR4, null); - return VersionConvertor_30_40.convertValueSet(expandedR4); + return convertValueSet(expandedR4); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -127,7 +133,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation @Override protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws FHIRException { org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; - valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet((ValueSet) theValueSet); + valueSetToExpandR4 = convertValueSet((ValueSet) theValueSet); return valueSetToExpandR4; } @@ -139,7 +145,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; valueSetToExpandR4 = toCanonicalValueSet(valueSetToExpand); org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(valueSetToExpandR4, theOffset, theCount); - return VersionConvertor_30_40.convertValueSet(expandedR4); + return convertValueSet(expandedR4); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -256,7 +262,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation public org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) { CodeSystem codeSystem = myValidationSupport.fetchCodeSystem(myContext, theSystem); try { - return VersionConvertor_30_40.convertCodeSystem(codeSystem); + return convertCodeSystem(codeSystem); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -323,7 +329,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation public ValidateCodeResult validateCodeIsInPreExpandedValueSet(IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = VersionConvertor_30_40.convertValueSet(valueSet); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = convertValueSet(valueSet); Coding coding = (Coding) theCoding; org.hl7.fhir.r4.model.Coding codingR4 = null; @@ -347,7 +353,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) { ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null"); ValueSet valueSet = (ValueSet) theValueSet; - org.hl7.fhir.r4.model.ValueSet valueSetR4 = VersionConvertor_30_40.convertValueSet(valueSet); + org.hl7.fhir.r4.model.ValueSet valueSetR4 = convertValueSet(valueSet); return super.isValueSetPreExpandedForCodeValidation(valueSetR4); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java index 6449945ddb2..a4f8ab5136b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java @@ -90,21 +90,21 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup super.throwInvalidValueSet(theValueSet); } - return expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR5), null); + return expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetR5), null); } @Override public IBaseResource expandValueSet(IBaseResource theInput) { org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theInput); org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSetInMemory(valueSetToExpand, null); - return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR4); + return org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetR4); } @Override public IBaseResource expandValueSet(IBaseResource theInput, int theOffset, int theCount) { org.hl7.fhir.r4.model.ValueSet valueSetToExpand = toCanonicalValueSet(theInput); org.hl7.fhir.r4.model.ValueSet valueSetR4 = super.expandValueSet(valueSetToExpand, theOffset, theCount); - return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR4); + return org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetR4); } @Override @@ -117,8 +117,8 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { ValueSet valueSetToExpand = new ValueSet(); valueSetToExpand.getCompose().addInclude(theInclude); - org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSetInMemory(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand), null); - return new ValueSetExpander.ValueSetExpansionOutcome(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expandedR4)); + org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSetInMemory(org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetToExpand), null); + return new ValueSetExpander.ValueSetExpansionOutcome(org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expandedR4)); } @Override @@ -199,13 +199,13 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup @Override public org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) { CodeSystem codeSystemR5 = myValidationSupport.fetchCodeSystem(myContext, theSystem); - return org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(codeSystemR5); + return org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem(codeSystemR5); } @Override protected org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) { ValueSet valueSetR5 = myValueSetResourceDao.toResource(ValueSet.class, theResourceTable, null, false); - return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetR5); + return org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSetR5); } @Override @@ -277,7 +277,7 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup @Override protected org.hl7.fhir.r4.model.ValueSet toCanonicalValueSet(IBaseResource theValueSet) throws org.hl7.fhir.exceptions.FHIRException { - return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet((ValueSet) theValueSet); + return org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet((ValueSet) theValueSet); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java index 925d0503811..5fce60cfa30 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java @@ -37,6 +37,9 @@ import org.springframework.context.event.ContextStartedEvent; import org.springframework.context.event.EventListener; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; +import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap; +import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl implements ITermVersionAdapterSvc { @@ -72,7 +75,7 @@ public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl im public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { CodeSystem resourceToStore; try { - resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource); + resourceToStore = convertCodeSystem(theCodeSystemResource); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -89,7 +92,7 @@ public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl im public void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) { ConceptMap resourceToStore; try { - resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap); + resourceToStore = convertConceptMap(theConceptMap); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -105,7 +108,7 @@ public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl im public void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) { ValueSet valueSetDstu3; try { - valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet); + valueSetDstu3 = convertValueSet(theValueSet); } catch (FHIRException e) { throw new InternalErrorException(e); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java index f0f653d3667..83cc9c0ae76 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java @@ -62,7 +62,7 @@ public class TermVersionAdapterSvcR5 extends BaseTermVersionAdapterSvcImpl imple public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { validateCodeSystemForStorage(theCodeSystemResource); - CodeSystem codeSystemR4 = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(theCodeSystemResource); + CodeSystem codeSystemR4 = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem(theCodeSystemResource); if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); return myCodeSystemResourceDao.update(codeSystemR4, matchUrl).getId(); @@ -74,7 +74,7 @@ public class TermVersionAdapterSvcR5 extends BaseTermVersionAdapterSvcImpl imple @Override public void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) { - ConceptMap conceptMapR4 = org.hl7.fhir.convertors.conv40_50.ConceptMap.convertConceptMap(theConceptMap); + ConceptMap conceptMapR4 = org.hl7.fhir.convertors.conv40_50.ConceptMap40_50.convertConceptMap(theConceptMap); if (isBlank(theConceptMap.getIdElement().getIdPart())) { String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); @@ -87,7 +87,7 @@ public class TermVersionAdapterSvcR5 extends BaseTermVersionAdapterSvcImpl imple @Override public void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) { - ValueSet valueSetR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(theValueSet); + ValueSet valueSetR4 = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(theValueSet); if (isBlank(theValueSet.getIdElement().getIdPart())) { String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java index 58e7195a3bc..01e55dcf267 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java @@ -44,14 +44,11 @@ public interface ITermLoaderSvc { UploadStatistics loadSnomedCt(List theFiles, RequestDetails theRequestDetails); - // FIXME: remove the default implementation before 4.1.0 - default UploadStatistics loadCustom(String theSystem, List theFiles, RequestDetails theRequestDetails) { return null; }; + UploadStatistics loadCustom(String theSystem, List theFiles, RequestDetails theRequestDetails); - // FIXME: remove the default implementation before 4.1.0 - default UploadStatistics loadDeltaAdd(String theSystem, List theFiles, RequestDetails theRequestDetails) { return null; }; + UploadStatistics loadDeltaAdd(String theSystem, List theFiles, RequestDetails theRequestDetails); - // FIXME: remove the default implementation before 4.1.0 - default UploadStatistics loadDeltaRemove(String theSystem, List theFiles, RequestDetails theRequestDetails) { return null; }; + UploadStatistics loadDeltaRemove(String theSystem, List theFiles, RequestDetails theRequestDetails); interface FileDescriptor { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index 6c7c2c14427..ecf3dce5c73 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -86,7 +86,7 @@ public class LoincHandler implements IRecordHandler { concept.addPropertyString(nextPropertyName, nextPropertyValue); break; case CODING: - // FIXME: handle "Ser/Plas^Donor" + // TODO: handle "Ser/Plas^Donor" String propertyValue = nextPropertyValue; if (nextPropertyName.equals("COMPONENT")) { if (propertyValue.contains("^")) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java index df32f31e341..f02a9203011 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java @@ -1,9 +1,5 @@ package ca.uhn.fhir.jpa.dao.dstu2; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; @@ -32,7 +28,11 @@ import org.junit.Before; import org.junit.Test; import java.io.IOException; -import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2ValidateTest.class); @@ -94,15 +94,14 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { myStructureDefinitionDao.create(sd, mySrd); Observation input = new Observation(); - ResourceMetadataKeyEnum.PROFILES.put(input, Arrays.asList(new IdDt(sd.getUrl()))); + ResourceMetadataKeyEnum.PROFILES.put(input, Collections.singletonList(new IdDt(sd.getUrl()))); input.addIdentifier().setSystem("http://acme").setValue("12345"); input.getEncounter().setReference("http://foo.com/Encounter/9"); input.setStatus(ObservationStatusEnum.FINAL); input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345"); - String encoded = null; - MethodOutcome outcome = null; + String encoded; ValidationModeEnum mode = ValidationModeEnum.CREATE; switch (enc) { case JSON: @@ -130,12 +129,12 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { } @Test - public void testValidateResourceContainingProfileDeclarationInvalid() throws Exception { + public void testValidateResourceContainingProfileDeclarationInvalid() { String methodName = "testValidateResourceContainingProfileDeclarationInvalid"; Observation input = new Observation(); String profileUri = "http://example.com/StructureDefinition/" + methodName; - ResourceMetadataKeyEnum.PROFILES.put(input, Arrays.asList(new IdDt(profileUri))); + ResourceMetadataKeyEnum.PROFILES.put(input, Collections.singletonList(new IdDt(profileUri))); input.addIdentifier().setSystem("http://acme").setValue("12345"); input.getEncounter().setReference("http://foo.com/Encounter/9"); @@ -148,7 +147,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome()); ourLog.info(ooString); - assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved")); + assertThat(ooString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index 45bb5e84837..bdc3ad3a2d7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -35,7 +35,6 @@ import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.Search; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.exceptions.FHIRException; @@ -60,6 +59,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; +import static org.hl7.fhir.convertors.conv30_40.ConceptMap30_40.convertConceptMap; import static org.junit.Assert.fail; @RunWith(SpringJUnit4ClassRunner.class) @@ -386,7 +386,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { */ public static ConceptMap createConceptMap() { try { - return VersionConvertor_30_40.convertConceptMap(BaseJpaR4Test.createConceptMap()); + return convertConceptMap(BaseJpaR4Test.createConceptMap()); } catch (FHIRException fe) { throw new InternalErrorException(fe); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index 3619811ca47..7430378c272 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -15,6 +15,7 @@ import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; +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 org.hl7.fhir.r5.utils.IResourceValidator; @@ -99,7 +100,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { myValueSetDao.create(vs); ValueSet expansion = myValueSetDao.expandByIdentifier("http://ccim.on.ca/fhir/iar/ValueSet/iar-citizenship-status", null); - ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); // Questionnaire q = loadResourceFromClasspath(Questionnaire.class,"/dstu3/iar/Questionnaire-iar-test.xml" ); // myQuestionnaireDao.create(q); @@ -322,15 +323,11 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { ValidationModeEnum mode = ValidationModeEnum.CREATE; String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input); - try { - myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); - fail(); - } catch (PreconditionFailedException e) { - String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()); - ourLog.info(ooString); - assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved")); - } - + MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); + OperationOutcome oo = (OperationOutcome) output.getOperationOutcome(); + String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); + ourLog.info(outputString); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 7dfffc7dfb9..a3f8a136228 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -64,7 +64,6 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityManager; import java.io.IOException; @@ -409,7 +408,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { } @Before - @Transactional() public void beforePurgeDatabase() { purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataExportSvc); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java index fcd0961bd9f..c81ca66f935 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java @@ -32,7 +32,7 @@ import javax.annotation.Nullable; import java.util.Date; import java.util.concurrent.atomic.AtomicLong; -import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.DEFAULT_CUTOFF_SLACK; +import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.*; @@ -47,7 +47,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { @After() public void after() { DatabaseSearchCacheSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(mySearchCacheSvc); - staleSearchDeletingSvc.setCutoffSlackForUnitTest(DEFAULT_CUTOFF_SLACK); + staleSearchDeletingSvc.setCutoffSlackForUnitTest(SEARCH_CLEANUP_JOB_INTERVAL_MILLIS); DatabaseSearchCacheSvcImpl.setNowForUnitTests(null); } @@ -81,8 +81,11 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } Thread.sleep(10); - myDaoConfig.setExpireSearchResultsAfterMillis(1000L); - myDaoConfig.setReuseCachedSearchResultsForMillis(500L); + long reuseCachedSearchResultsForMillis = 500L; + myDaoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsForMillis); + long millisBetweenReuseAndExpire = 800L; + long expireSearchResultsAfterMillis = 1000L; + myDaoConfig.setExpireSearchResultsAfterMillis(expireSearchResultsAfterMillis); long start = System.currentTimeMillis(); DatabaseSearchCacheSvcImpl.setNowForUnitTests(start); @@ -107,9 +110,9 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } assertEquals(searchUuid1, searchUuid2); - TestUtil.sleepAtLeast(501); + TestUtil.sleepAtLeast(reuseCachedSearchResultsForMillis + 1); - // We're now past 500ms so we shouldn't reuse the search + // We're now past reuseCachedSearchResultsForMillis so we shouldn't reuse the search final String searchUuid3; { @@ -124,35 +127,36 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { // Search just got used so it shouldn't be deleted - DatabaseSearchCacheSvcImpl.setNowForUnitTests(start + 500); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(start + reuseCachedSearchResultsForMillis); + final AtomicLong search1timestamp = new AtomicLong(); + final AtomicLong search2timestamp = new AtomicLong(); final AtomicLong search3timestamp = new AtomicLong(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - Search search3 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).orElseThrow(()->new InternalErrorException("Search doesn't exist")); - assertNotNull(search3); + Search search1 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).orElseThrow(()->new InternalErrorException("Search doesn't exist")); + assertNotNull(search1); + search1timestamp.set(search1.getCreated().getTime()); Search search2 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid2).orElseThrow(()->new InternalErrorException("Search doesn't exist")); assertNotNull(search2); - search3timestamp.set(search2.getSearchLastReturned().getTime()); + search2timestamp.set(search2.getCreated().getTime()); + Search search3 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).orElseThrow(()->new InternalErrorException("Search doesn't exist")); + assertNotNull(search3); + search3timestamp.set(search3.getCreated().getTime()); } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 800); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(search1timestamp.get() + millisBetweenReuseAndExpire); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - assertNotNull(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3)); - } - }); - newTxTemplate().execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - assertNotNull(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1)); + assertTrue(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).isPresent()); + assertTrue(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).isPresent()); } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 1100); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(search1timestamp.get() + reuseCachedSearchResultsForMillis + expireSearchResultsAfterMillis + 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @@ -163,14 +167,12 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 2100); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + reuseCachedSearchResultsForMillis + expireSearchResultsAfterMillis + 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); await().until(()-> newTxTemplate().execute(t -> !mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).isPresent())); await().until(()-> newTxTemplate().execute(t -> !mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).isPresent())); - - } @Test @@ -197,7 +199,6 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { params.add(Patient.SP_FAMILY, new StringParam("EXPIRE")); final IBundleProvider bundleProvider = myPatientDao.search(params); assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2)); - assertThat(toUnqualifiedVersionlessIds(bundleProvider), containsInAnyOrder(pid1, pid2)); waitForSearchToSave(bundleProvider.getUuid()); final AtomicLong start = new AtomicLong(); @@ -213,9 +214,11 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } }); - myDaoConfig.setExpireSearchResultsAfterMillis(500); - myDaoConfig.setReuseCachedSearchResultsForMillis(500L); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(start.get() + 499); + int expireSearchResultsAfterMillis = 700; + myDaoConfig.setExpireSearchResultsAfterMillis(expireSearchResultsAfterMillis); + long reuseCachedSearchResultsForMillis = 400L; + myDaoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsForMillis); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(start.get() + expireSearchResultsAfterMillis - 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); txTemplate.execute(new TransactionCallbackWithoutResult() { @Override @@ -224,7 +227,7 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(start.get() + 600); + DatabaseSearchCacheSvcImpl.setNowForUnitTests(start.get() + expireSearchResultsAfterMillis + reuseCachedSearchResultsForMillis + 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); txTemplate.execute(new TransactionCallbackWithoutResult() { @Override @@ -251,8 +254,12 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } Thread.sleep(10); - myDaoConfig.setExpireSearchResultsAfterMillis(1000L); - myDaoConfig.setReuseCachedSearchResultsForMillis(500L); + long expireSearchResultsAfterMillis = 1000L; + myDaoConfig.setExpireSearchResultsAfterMillis(expireSearchResultsAfterMillis); + + long reuseCachedSearchResultsForMillis = 500L; + myDaoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsForMillis); + long start = System.currentTimeMillis(); final String searchUuid1; @@ -278,9 +285,10 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } assertEquals(searchUuid1, searchUuid2); - TestUtil.sleepAtLeast(501); + TestUtil.sleepAtLeast(reuseCachedSearchResultsForMillis + 1); + myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); - // We're now past 500ms so we shouldn't reuse the search + // We're now past reuseCachedSearchResultsForMillis so we shouldn't reuse the search final String searchUuid3; { @@ -293,37 +301,35 @@ public class FhirResourceDaoR4SearchPageExpiryTest extends BaseJpaR4Test { } assertNotEquals(searchUuid1, searchUuid3); - // Search just got used so it shouldn't be deleted + waitForSearchToSave(searchUuid3); + + // Search hasn't expired yet so it shouldn't be deleted myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); + final AtomicLong search1timestamp = new AtomicLong(); final AtomicLong search3timestamp = new AtomicLong(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { + Search search1 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).orElseThrow(()->new InternalErrorException("Search doesn't exist")); Search search3 = mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).orElseThrow(()->new InternalErrorException("Search doesn't exist")); assertNotNull(search3); + search1timestamp.set(search1.getCreated().getTime()); search3timestamp.set(search3.getCreated().getTime()); } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 800); - + DatabaseSearchCacheSvcImpl.setNowForUnitTests(search1timestamp.get() + expireSearchResultsAfterMillis + reuseCachedSearchResultsForMillis + 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); - newTxTemplate().execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus theArg0) { - assertNotNull(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3)); - } - }); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus theArg0) { assertFalse(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid1).isPresent()); + assertTrue(mySearchEntityDao.findByUuidAndFetchIncludes(searchUuid3).isPresent()); } }); - DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + 2100); - + DatabaseSearchCacheSvcImpl.setNowForUnitTests(search3timestamp.get() + expireSearchResultsAfterMillis + reuseCachedSearchResultsForMillis + 1); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); newTxTemplate().execute(new TransactionCallbackWithoutResult() { @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java index bfe7f9cba39..23ee3628fc5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java @@ -47,7 +47,6 @@ import static org.junit.Assert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestR4ConfigWithElasticSearch.class}) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -@Ignore // FIXME: remove public class FhirResourceDaoR4SearchWithElasticSearchTest extends BaseJpaTest { 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"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 74bfa659695..2351887144f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -395,15 +395,11 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { ValidationModeEnum mode = ValidationModeEnum.CREATE; String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input); - try { - myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); - fail(); - } catch (PreconditionFailedException e) { - String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()); - ourLog.info(ooString); - assertThat(ooString, containsString("StructureDefinition reference \\\"" + profileUri + "\\\" could not be resolved")); - } - + MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd); + org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) output.getOperationOutcome(); + String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); + ourLog.info(outputString); + assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked")); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 69a03290a8a..45edc4928c7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; +import ca.uhn.fhir.jpa.util.CoordCalculatorTest; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; @@ -65,6 +66,7 @@ import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -3003,7 +3005,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { .count(5) .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid1 = toSearchUuidFromLinkNext(result1); Search search1 = newTxTemplate().execute(new TransactionCallback() { @@ -3012,7 +3013,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { return mySearchEntityDao.findByUuidAndFetchIncludes(uuid1).orElseThrow(() -> new InternalErrorException("")); } }); - Date lastReturned1 = search1.getSearchLastReturned(); + Date created1 = search1.getCreated(); Bundle result2 = ourClient .search() @@ -3021,7 +3022,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { .count(5) .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid2 = toSearchUuidFromLinkNext(result2); Search search2 = newTxTemplate().execute(new TransactionCallback() { @@ -3030,9 +3030,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { return mySearchEntityDao.findByUuidAndFetchIncludes(uuid2).orElseThrow(() -> new InternalErrorException("")); } }); - Date lastReturned2 = search2.getSearchLastReturned(); + Date created2 = search2.getCreated(); - assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); + assertEquals(created2.getTime(), created1.getTime()); Thread.sleep(1500); @@ -3067,24 +3067,22 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { .forResource("Organization") .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid1 = toSearchUuidFromLinkNext(result1); Search search1 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid1).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned1 = search1.getSearchLastReturned(); + Date created1 = search1.getCreated(); Bundle result2 = ourClient .search() .forResource("Organization") .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid2 = toSearchUuidFromLinkNext(result2); Search search2 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid2).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned2 = search2.getSearchLastReturned(); + Date created2 = search2.getCreated(); - assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); + assertEquals(created2.getTime(), created1.getTime()); assertEquals(uuid1, uuid2); } @@ -4282,6 +4280,55 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } + @Test + public void testNearSearchApproximate() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(locId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + + } + private String toStr(Date theDate) { return new InstantDt(theDate).getValueAsString(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 22a94634be4..a8f485ba05d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -14,27 +14,15 @@ import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.PreferReturnEnum; -import ca.uhn.fhir.rest.api.SearchTotalModeEnum; -import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.gclient.StringClientParam; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.NumberParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; @@ -46,29 +34,16 @@ import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.*; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicNameValuePair; import org.hamcrest.Matchers; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseBundle; -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 org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; -import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.hl7.fhir.r4.model.Bundle.HTTPVerb; -import org.hl7.fhir.r4.model.Bundle.SearchEntryMode; +import org.hl7.fhir.r4.model.Bundle.*; import org.hl7.fhir.r4.model.Encounter.EncounterLocationComponent; import org.hl7.fhir.r4.model.Encounter.EncounterStatus; import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; @@ -77,11 +52,7 @@ import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.TransactionStatus; @@ -96,28 +67,14 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.stream.Collectors; import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast; import static ca.uhn.fhir.jpa.util.TestUtil.sleepOneClick; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; @SuppressWarnings("Duplicates") public class ResourceProviderR4Test extends BaseResourceProviderR4Test { @@ -4115,11 +4072,10 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .count(5) .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid1 = toSearchUuidFromLinkNext(result1); Search search1 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid1).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned1 = search1.getSearchLastReturned(); + Date created1 = search1.getCreated(); Bundle result2 = ourClient .search() @@ -4128,13 +4084,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .count(5) .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid2 = toSearchUuidFromLinkNext(result2); Search search2 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid2).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned2 = search2.getSearchLastReturned(); + Date created2 = search2.getCreated(); - assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); + assertEquals(created2.getTime(), created1.getTime()); Thread.sleep(1500); @@ -4145,7 +4100,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .count(5) .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); String uuid3 = toSearchUuidFromLinkNext(result3); @@ -4170,11 +4124,10 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .forResource("Organization") .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid1 = toSearchUuidFromLinkNext(result1); Search search1 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid1).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned1 = search1.getSearchLastReturned(); + Date created1 = search1.getCreated(); sleepOneClick(); @@ -4183,13 +4136,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .forResource("Organization") .returnBundle(Bundle.class) .execute(); - mySearchCacheSvc.flushLastUpdated(); final String uuid2 = toSearchUuidFromLinkNext(result2); Search search2 = newTxTemplate().execute(theStatus -> mySearchEntityDao.findByUuidAndFetchIncludes(uuid2).orElseThrow(() -> new InternalErrorException(""))); - Date lastReturned2 = search2.getSearchLastReturned(); + Date created2 = search2.getCreated(); - assertTrue(lastReturned2.getTime() > lastReturned1.getTime()); + assertEquals(created2.getTime(), created1.getTime()); assertEquals(uuid1, uuid2); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java index a760af6a195..c66c824022c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java @@ -9,12 +9,10 @@ import ca.uhn.fhir.jpa.entity.SearchResult; import ca.uhn.fhir.jpa.entity.SearchTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; -import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl; import ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl; import ca.uhn.fhir.rest.gclient.IClientExecutable; import ca.uhn.fhir.rest.gclient.IQuery; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.r4.model.Bundle; @@ -25,7 +23,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.TestPropertySource; import org.springframework.test.util.AopTestUtils; import java.util.Date; @@ -50,7 +47,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { public void after() throws Exception { super.after(); DatabaseSearchCacheSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(mySearchCacheSvc); - staleSearchDeletingSvc.setCutoffSlackForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_CUTOFF_SLACK); + staleSearchDeletingSvc.setCutoffSlackForUnitTest(DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS); } @@ -121,7 +118,6 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setCreated(DateUtils.addDays(new Date(), -10000)); search.setSearchType(SearchTypeEnum.SEARCH); search.setResourceType("Patient"); - search.setSearchLastReturned(DateUtils.addDays(new Date(), -10000)); search = mySearchEntityDao.save(search); for (int i = 0; i < 15; i++) { @@ -163,7 +159,6 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setCreated(DateUtils.addDays(new Date(), -10000)); search.setSearchType(SearchTypeEnum.SEARCH); search.setResourceType("Patient"); - search.setSearchLastReturned(DateUtils.addDays(new Date(), -10000)); mySearchEntityDao.save(search); }); @@ -190,7 +185,6 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setCreated(DateUtils.addDays(new Date(), -10000)); search.setSearchType(SearchTypeEnum.SEARCH); search.setResourceType("Patient"); - search.setSearchLastReturned(DateUtils.addDays(new Date(), -10000)); search = mySearchEntityDao.save(search); }); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index bb5a68b22e9..cfb73e25a0b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -1,8 +1,12 @@ package ca.uhn.fhir.jpa.provider.r4; +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.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import ca.uhn.fhir.model.api.annotation.SimpleSetter; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.TestUtil; @@ -21,6 +25,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -253,6 +258,67 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes ); } + @Test + public void testApplyDeltaAdd_UsingCodeSystemWithComma() throws IOException { + + // Create initial codesystem + { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + codeSystem.setUrl("https://good.health"); + + LoggingInterceptor interceptor = new LoggingInterceptor(true); + ourClient.registerInterceptor(interceptor); + ourClient + .create() + .resource(codeSystem) + .execute(); + ourClient.unregisterInterceptor(interceptor); + } + + // Add a child with a really long description + Parameters outcome; + { + Parameters inputBundle = loadResourceFromClasspath(Parameters.class, "/term-delta-json.json"); + + LoggingInterceptor interceptor = new LoggingInterceptor(true); + ourClient.registerInterceptor(interceptor); + outcome = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) + .withParameters(inputBundle) + .execute(); + ourClient.unregisterInterceptor(interceptor); + } + + String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); + ourLog.info(encoded); + assertThat(encoded, stringContainsInOrder( + "\"name\": \"conceptCount\"", + "\"valueInteger\": 2", + "\"name\": \"target\"", + "\"reference\": \"CodeSystem/" + )); + + assertHierarchyContains( + "1111222233 seq=0", + " 1111222234 seq=0" + ); + + runInTransaction(()->{ + TermCodeSystem codeSystem = myTermCodeSystemDao.findByCodeSystemUri("https://good.health"); + TermCodeSystemVersion version = codeSystem.getCurrentVersion(); + TermConcept code = myTermConceptDao.findByCodeSystemAndCode(version, "1111222233").get(); + assertEquals("Some label for the parent - with a dash too", code.getDisplay()); + + code = myTermConceptDao.findByCodeSystemAndCode(version, "1111222234").get(); + assertEquals("Some very very very very very looooooong child label with a coma, another one, one more, more and final one", code.getDisplay()); + }); + } + + + @Test public void testApplyDeltaAdd_UsingCodeSystemWithVeryLongDescription() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index 17cf99e22ff..a622a7a4933 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -182,41 +182,17 @@ public class SearchCoordinatorSvcImplTest { when(mySearchBuilder.createQuery(any(), any(), any())).thenReturn(iter); doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); - when(mySearchResultCacheSvc.fetchResultPids(any(), anyInt(), anyInt())).thenAnswer(t -> { - List returnedValues = iter.getReturnedValues(); - int offset = t.getArgument(1, Integer.class); - int end = t.getArgument(2, Integer.class); - end = Math.min(end, returnedValues.size()); - offset = Math.min(offset, returnedValues.size()); - ourLog.info("findWithSearchUuid {} - {} out of {} values", offset, end, returnedValues.size()); - return returnedValues.subList(offset, end); - }); - - when(mySearchResultCacheSvc.fetchAllResultPids(any())).thenReturn(allResults); - - when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any())).thenAnswer(t->{ + when(mySearchCacheSvc.save(any())).thenAnswer(t -> { Search search = t.getArgument(0, Search.class); - assertEquals(SearchStatusEnum.PASSCMPLET, search.getStatus()); - search.setStatus(SearchStatusEnum.LOADING); - return Optional.of(search); + myCurrentSearch = search; + return search; }); IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null); assertNotNull(result.getUuid()); assertEquals(null, result.size()); - List resources; - - when(mySearchCacheSvc.save(any())).thenAnswer(t -> { - Search search = t.getArgument(0, Search.class); - myCurrentSearch = search; - return search; - }); - when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> Optional.ofNullable(myCurrentSearch)); - IFhirResourceDao dao = myCallingDao; - when(myDaoRegistry.getResourceDao(any(String.class))).thenReturn(dao); - - resources = result.getResources(0, 100000); + List resources = result.getResources(0, 100000); assertEquals(790, resources.size()); assertEquals("10", resources.get(0).getIdElement().getValueAsString()); assertEquals("799", resources.get(789).getIdElement().getValueAsString()); @@ -604,6 +580,11 @@ public class SearchCoordinatorSvcImplTest { return myWrap.getSkippedCount(); } + @Override + public int getNonSkippedCount() { + return myCount; + } + @Override public void close() { // nothing @@ -613,6 +594,7 @@ public class SearchCoordinatorSvcImplTest { public static class ResultIterator extends BaseIterator implements IResultIterator { private final Iterator myWrap; + private int myCount; ResultIterator(Iterator theWrap) { myWrap = theWrap; @@ -625,6 +607,7 @@ public class SearchCoordinatorSvcImplTest { @Override public ResourcePersistentId next() { + myCount++; return myWrap.next(); } @@ -633,6 +616,11 @@ public class SearchCoordinatorSvcImplTest { return 0; } + @Override + public int getNonSkippedCount() { + return myCount; + } + @Override public void close() { // nothing @@ -699,6 +687,11 @@ public class SearchCoordinatorSvcImplTest { } } + @Override + public int getNonSkippedCount() { + return 0; + } + @Override public void close() { // nothing diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index c12fad6d306..e79e49fe76e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -58,6 +58,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { Location.SP_NEAR + "=1000.0:2000.0" + "&" + Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); + map.setLocationDistance(); QuantityParam nearDistanceParam = map.getNearDistanceParam(); assertEquals(1, map.size()); @@ -74,6 +75,8 @@ public class MatchUrlServiceTest extends BaseJpaTest { "&" + Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); + map.setLocationDistance(); + fail(); } catch (IllegalArgumentException e) { assertEquals("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present", e.getMessage()); @@ -89,7 +92,8 @@ public class MatchUrlServiceTest extends BaseJpaTest { "," + "2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setLoadSynchronous(true); + map.setLocationDistance(); + fail(); } catch (IllegalArgumentException e) { assertEquals("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present", e.getMessage()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java index 20b7cc1f52b..8ce8bb95fc7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java @@ -17,7 +17,6 @@ import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; @@ -39,14 +38,14 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -56,10 +55,6 @@ import static org.junit.Assert.fail; * Test the rest-hook subscriptions */ @SuppressWarnings("Duplicates") -@TestPropertySource(properties = { - UnregisterScheduledProcessor.SCHEDULING_DISABLED + "=false" -}) -@DirtiesContext public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionTriggeringDstu3Test.class); @@ -194,6 +189,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); waitForQueueToDrain(); + + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(0, ourCreatedObservations); waitForSize(2, ourUpdatedObservations); @@ -248,6 +246,13 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te responseValue = response.getParameter().get(0).getValue().primitiveValue(); assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(33, ourUpdatedObservations); + + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); waitForSize(51, ourUpdatedObservations); waitForSize(0, ourCreatedObservations); waitForSize(0, ourCreatedPatients); @@ -257,8 +262,6 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te @Test public void testTriggerUsingOrSeparatedList_MultipleStrings() throws Exception { - myDaoConfig.setSearchPreFetchThresholds(Lists.newArrayList(13, 22, 100)); - String payload = "application/fhir+json"; IdType sub2id = createSubscription("Patient?", payload, ourListenerServerBase).getIdElement(); @@ -284,8 +287,18 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te String responseValue = response.getParameter().get(0).getValue().primitiveValue(); assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); - waitForSize(0, ourCreatedPatients); - waitForSize(3, ourUpdatedPatients); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + assertEquals(0, mySubscriptionTriggeringSvc.getActiveJobCount()); + + assertEquals(0, ourCreatedPatients.size()); + await().until(() -> ourUpdatedPatients.size() == 3); } @@ -316,6 +329,8 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te String responseValue = response.getParameter().get(0).getValue().primitiveValue(); assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(0, ourCreatedPatients); waitForSize(3, ourUpdatedPatients); @@ -367,6 +382,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te responseValue = response.getParameter().get(0).getValue().primitiveValue(); assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(10, ourUpdatedObservations); waitForSize(0, ourCreatedObservations); waitForSize(0, ourCreatedPatients); @@ -421,6 +439,8 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te String responseValue = response.getParameter().get(0).getValue().primitiveValue(); assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(20, ourUpdatedObservations); waitForSize(0, ourCreatedObservations); waitForSize(0, ourCreatedPatients); @@ -458,6 +478,8 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); waitForQueueToDrain(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + waitForSize(0, ourCreatedObservations); waitForSize(1, ourUpdatedObservations); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/validator/ValidatorAcrossVersionsTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/validator/ValidatorAcrossVersionsTest.java index c335a1ac6ea..87fcc302bba 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/validator/ValidatorAcrossVersionsTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/validator/ValidatorAcrossVersionsTest.java @@ -54,8 +54,8 @@ public class ValidatorAcrossVersionsTest { ourLog.info(ctxDstu2.newJsonParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome())); assertEquals(2, result.getMessages().size()); - assertEquals("No questionnaire is identified, so no validation can be performed against the base questionnaire", result.getMessages().get(0).getMessage()); - assertEquals("Profile http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse, Element 'QuestionnaireResponse.status': minimum required = 1, but only found 0", result.getMessages().get(1).getMessage()); + assertEquals("Profile http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse, Element 'QuestionnaireResponse.status': minimum required = 1, but only found 0", result.getMessages().get(0).getMessage()); + assertEquals("No questionnaire is identified, so no validation can be performed against the base questionnaire", result.getMessages().get(1).getMessage()); } } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/term-delta-json.json b/hapi-fhir-jpaserver-base/src/test/resources/term-delta-json.json new file mode 100644 index 00000000000..78e18fccaaa --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/term-delta-json.json @@ -0,0 +1,30 @@ +{ + "resourceType": "Parameters", + "parameter": [ + { + "name": "system", + "valueUri": "https://good.health" + }, + { + "name": "codeSystem", + "resource": { + "resourceType": "CodeSystem", + "status": "active", + "content": "not-present", + "url": "https://good.health", + "concept": [ + { + "code": "1111222233", + "display": "Some label for the parent - with a dash too", + "concept": [ + { + "code": "1111222234", + "display": "Some very very very very very looooooong child label with a coma, another one, one more, more and final one" + } + ] + } + ] + } + } + ] +} diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index 345bf13d5ee..2820928227b 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java index a743ad281e7..8b60927345e 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java @@ -63,6 +63,8 @@ public class FlywayMigrator extends BaseMigrator { if (isDryRun()) { StringBuilder statementBuilder = buildExecutedStatementsString(); ourLog.info("SQL that would be executed:\n\n***********************************\n{}***********************************", statementBuilder); + } else { + ourLog.info("Schema migrated successfully."); } } catch (Exception e) { throw e; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java index e27668fd0fe..86d1d92bcd6 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/TaskOnlyMigrator.java @@ -51,7 +51,11 @@ public class TaskOnlyMigrator extends BaseMigrator { next.setConnectionProperties(connectionProperties); try { - ourLog.info("Executing task of type: {}", next.getClass().getSimpleName()); + if (isDryRun()) { + ourLog.info("Dry run {} {}", next.getFlywayVersion(), next.getDescription()); + } else { + ourLog.info("Executing {} {}", next.getFlywayVersion(), next.getDescription()); + } next.execute(); addExecutedStatements(next.getExecutedStatements()); } catch (SQLException e) { @@ -61,6 +65,8 @@ public class TaskOnlyMigrator extends BaseMigrator { if (isDryRun()) { StringBuilder statementBuilder = buildExecutedStatementsString(); ourLog.info("SQL that would be executed:\n\n***********************************\n{}***********************************", statementBuilder); + } else { + ourLog.info("Schema migrated successfully."); } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 51667c1374f..7638ed1b601 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -62,10 +62,12 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { protected void init420() { // 20191015 - present Builder version = forVersion(VersionEnum.V4_2_0); - // Eliminate circular dependency. - version.onTable("HFJ_RESOURCE").dropColumn("20200130.1", "FORCED_ID_PID"); - version.onTable("HFJ_RES_VER").dropColumn("20200130.2", "FORCED_ID_PID"); - version.onTable("HFJ_RES_VER").addForeignKey("20200130.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID"); + // TermValueSetConceptDesignation + version.onTable("TRM_VALUESET_C_DESIGNATION").dropIndex("20200202.1", "IDX_VALUESET_C_DSGNTN_VAL").failureAllowed(); + Builder.BuilderWithTableName searchTable = version.onTable("HFJ_SEARCH"); + searchTable.dropIndex("20200203.1", "IDX_SEARCH_LASTRETURNED"); + searchTable.dropColumn("20200203.2", "SEARCH_LAST_RETURNED"); + searchTable.addIndex("20200203.3", "IDX_SEARCH_CREATED").unique(false).withColumns("CREATED"); } protected void init410() { // 20190815 - 20191014 diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index a7de3803e1d..ea6431e8fbb 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -158,20 +158,22 @@ public class Builder { return myTableName; } - public void dropIndex(String theVersion, String theIndexName) { - dropIndexOptional(false, theVersion, theIndexName); + public BuilderCompleteTask dropIndex(String theVersion, String theIndexName) { + BaseTask task = dropIndexOptional(false, theVersion, theIndexName); + return new BuilderCompleteTask(task); } public void dropIndexStub(String theVersion, String theIndexName) { dropIndexOptional(true, theVersion, theIndexName); } - private void dropIndexOptional(boolean theDoNothing, String theVersion, String theIndexName) { + private DropIndexTask dropIndexOptional(boolean theDoNothing, String theVersion, String theIndexName) { DropIndexTask task = new DropIndexTask(myRelease, theVersion); task.setIndexName(theIndexName); task.setTableName(myTableName); task.setDoNothing(theDoNothing); addTask(task); + return task; } public void renameIndex(String theVersion, String theOldIndexName, String theNewIndexName) { @@ -286,11 +288,12 @@ public class Builder { withColumnsOptional(true, theColumnNames); } - public void withColumns(String... theColumnNames) { - withColumnsOptional(false, theColumnNames); + public BuilderCompleteTask withColumns(String... theColumnNames) { + BaseTask task = withColumnsOptional(false, theColumnNames); + return new BuilderCompleteTask(task); } - private void withColumnsOptional(boolean theDoNothing, String... theColumnNames) { + private AddIndexTask withColumnsOptional(boolean theDoNothing, String... theColumnNames) { AddIndexTask task = new AddIndexTask(myRelease, myVersion); task.setTableName(myTableName); task.setIndexName(myIndexName); @@ -298,6 +301,7 @@ public class Builder { task.setColumns(theColumnNames); task.setDoNothing(theDoNothing); addTask(task); + return task; } } } @@ -463,6 +467,12 @@ public class Builder { myTask.setFailureAllowed(true); return this; } + + public BuilderCompleteTask doNothing() { + myTask.setDoNothing(true); + return this; + } + } } diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 123f22b7a3a..83b438d3efc 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index a7570dcf71e..10d7189410a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -54,12 +54,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas private static final int MAX_PROFILE_LENGTH = 200; private static final long serialVersionUID = 1L; -// @Transient -// private transient byte[] myResource; -// -// @Transient -// private transient ResourceEncodingEnum myEncoding; - /** * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB */ diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 06f462936c4..709f74dfc10 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java index 6cd49534031..fb77ea8af24 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java @@ -26,13 +26,11 @@ import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.model.dstu2.resource.Location; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParameterUtil; -import ca.uhn.fhir.rest.param.QuantityAndListParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.UrlUtil; @@ -115,9 +113,6 @@ public class MatchUrlService { } else if (Constants.PARAM_SOURCE.equals(nextParamName)) { IQueryParameterAnd param = ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.TOKEN, nextParamName, paramList); paramMap.add(nextParamName, param); - } else if (Location.SP_NEAR_DISTANCE.equals(nextParamName)) { - QuantityAndListParam nearDistanceAndListParam = (QuantityAndListParam) ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.QUANTITY, nextParamName, paramList); - paramMap.setNearDistanceParam(nearDistanceAndListParam); } else if (nextParamName.startsWith("_")) { // ignore these since they aren't search params (e.g. _sort) } else { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index a159ce3b6fa..958e58c2f1c 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -5,15 +5,17 @@ import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.model.dstu2.resource.Location; import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hl7.fhir.dstu3.model.Location; import java.io.Serializable; import java.util.*; @@ -495,29 +497,38 @@ public class SearchParameterMap implements Serializable { } } - public void setNearDistanceParam(QuantityAndListParam theQuantityAndListParam) { - List orTokens = theQuantityAndListParam.getValuesAsQueryTokens(); - if (orTokens.isEmpty()) { - return; - } - if (orTokens.size() > 1) { - throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - QuantityOrListParam quantityOrListParam = orTokens.get(0); - List tokens = quantityOrListParam.getValuesAsQueryTokens(); - if (tokens.isEmpty()) { - return; - } - if (tokens.size() > 1) { - throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - myNearDistanceParam = tokens.get(0); + public void setNearDistanceParam(QuantityParam theQuantityParam) { + myNearDistanceParam = theQuantityParam; } public QuantityParam getNearDistanceParam() { return myNearDistanceParam; } + public void setLocationDistance() { + if (containsKey(Location.SP_NEAR_DISTANCE)) { + List> paramAndList = get(Location.SP_NEAR_DISTANCE); + + if (paramAndList.isEmpty()) { + return; + } + if (paramAndList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + List paramOrList = paramAndList.get(0); + if (paramOrList.isEmpty()) { + return; + } + if (paramOrList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + setNearDistanceParam((QuantityParam) paramOrList.get(0)); + + // Need to remove near-distance or it we'll get a hashcode predicate for it + remove(Location.SP_NEAR_DISTANCE); + } + } + public enum EverythingModeEnum { /* * Don't reorder! We rely on the ordinals diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java index 9626f14282e..d27f51fc814 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java @@ -26,12 +26,16 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport; +import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r5.hapi.ctx.IValidationSupport; -import org.hl7.fhir.r5.model.*; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.ResourceType; +import org.hl7.fhir.r5.model.TypeDetails; +import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; import java.util.Collections; @@ -115,7 +119,7 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements } @Override - public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException { + public Base resolveReference(Object appContext, String theUrl, Base refContext) throws FHIRException { /* * When we're doing resolution within the SearchParamExtractor, if we want diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java index c93c5e14ba8..d83eb5048c9 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.MetaUtil; import ca.uhn.fhir.util.UrlUtil; +import org.hl7.fhir.dstu3.model.Location; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -88,7 +89,7 @@ public class InMemoryResourceMatcher { if (searchParameterMap.getLastUpdated() != null) { return InMemoryMatchResult.unsupportedFromParameterAndReason(Constants.PARAM_LASTUPDATED, InMemoryMatchResult.STANDARD_PARAMETER); } - if (searchParameterMap.getNearDistanceParam() != null) { + if (searchParameterMap.containsKey(Location.SP_NEAR)) { return InMemoryMatchResult.unsupportedFromReason(InMemoryMatchResult.LOCATION_NEAR); } diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 19a6c3caf27..a7e80f6ec98 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 32f3752d80e..e6234a4fec8 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml @@ -157,7 +157,7 @@ ca.uhn.hapi.fhir hapi-fhir-converter - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 56d24a8322d..7e17a896660 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java index dd5896228a1..857666e2375 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java @@ -19,17 +19,21 @@ package ca.uhn.fhir.rest.api.server; * limitations under the License. * #L% */ -import java.io.*; + +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.SummaryEnum; +import org.hl7.fhir.instance.model.api.IBaseBinary; +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 java.io.IOException; +import java.io.Writer; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; -import org.hl7.fhir.instance.model.api.*; - -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.SummaryEnum; - public interface IRestfulResponse { Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean thePrettyPrint, Set theSummaryMode, int theStatusCode, String theStatusMessage, boolean theRespondGzip, boolean theAddContentLocation) throws IOException; @@ -40,7 +44,7 @@ public interface IRestfulResponse { */ Object returnResponse(ParseAction outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException; - Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException; + Writer getResponseWriter(int theStatusCode, String theStatusMessage, String theContentType, String theCharset, boolean theRespondGzip) throws IOException; Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 08cd779442c..3c32507105a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -360,11 +360,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer supertype = clazz.getSuperclass(); while (!Object.class.equals(supertype)) { count += findResourceMethods(theProvider, supertype); + count += findResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces()); supertype = supertype.getSuperclass(); } try { count += findResourceMethods(theProvider, clazz); + count += findResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces()); } catch (ConfigurationException e) { throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage(), e); } @@ -373,6 +375,15 @@ public class RestfulServer extends HttpServlet implements IRestfulServer[] interfaces) { + int count = 0; + for (Class anInterface : interfaces) { + count += findResourceMethods(theProvider, anInterface); + count += findResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces()); + } + return count; + } + private int findResourceMethods(Object theProvider, Class clazz) throws ConfigurationException { int count = 0; @@ -1604,14 +1615,23 @@ public class RestfulServer extends HttpServlet implements IRestfulServer resourceNames = new ArrayList<>(); while (!Object.class.equals(supertype)) { removeResourceMethods(theProvider, supertype, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces(), resourceNames); supertype = supertype.getSuperclass(); } removeResourceMethods(theProvider, clazz, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces(), resourceNames); for (String resourceName : resourceNames) { myResourceNameToBinding.remove(resourceName); } } + private void removeResourceMethodsOnInterfaces(Object theProvider, Class[] interfaces, Collection resourceNames) { + for (Class anInterface : interfaces) { + removeResourceMethods(theProvider, anInterface, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces(), resourceNames); + } + } + /* * Collect the set of RESTful methods for a single class when it is being unregistered */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index d21620f5c79..0ef51233a04 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -28,11 +28,16 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.PreferHeader; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.ElementsParameter; @@ -40,7 +45,13 @@ import ca.uhn.fhir.rest.server.method.SummaryEnumParameter; import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.UrlUtil; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseBinary; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import javax.annotation.Nonnull; import javax.servlet.http.HttpServletRequest; @@ -51,7 +62,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.replace; +import static org.apache.commons.lang3.StringUtils.trim; public class RestfulServerUtils { static final Pattern ACCEPT_HEADER_PATTERN = Pattern.compile("\\s*([a-zA-Z0-9+.*/-]+)\\s*(;\\s*([a-zA-Z]+)\\s*=\\s*([a-zA-Z0-9.]+)\\s*)?(,?)"); @@ -754,12 +768,12 @@ public class RestfulServerUtils { } public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int stausCode, boolean theAddContentLocationHeader, - boolean respondGzip, RequestDetails theRequestDetails) throws IOException { + boolean respondGzip, RequestDetails theRequestDetails) throws IOException { return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, null, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null); } public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set theSummaryMode, int theStatusCode, String theStatusMessage, - boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) + boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType theOperationResourceLastUpdated) throws IOException { IRestfulResponse response = theRequestDetails.getResponse(); @@ -895,23 +909,10 @@ public class RestfulServerUtils { IParser parser = getNewParser(theServer.getFhirContext(), forVersion, theRequestDetails); parser.encodeResourceToWriter(theResource, writer); } - //FIXME resource leak + return response.sendWriterResponse(theStatusCode, contentType, charset, writer); } - // static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) { - // String countString = theRequest.getParameter(name); - // Integer count = null; - // if (isNotBlank(countString)) { - // try { - // count = Integer.parseInt(countString); - // } catch (NumberFormatException e) { - // ourLog.debug("Failed to parse _count value '{}': {}", countString, e); - // } - // } - // return count; - // } - public static String createEtag(String theVersionId) { return "W/\"" + theVersionId + '"'; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/IncludeParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/IncludeParameter.java index 83e253898f3..6e3cb7fa1fe 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/IncludeParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/IncludeParameter.java @@ -154,7 +154,7 @@ class IncludeParameter extends BaseQueryParameter { } return new Include(value, recurse); } - //FIXME null access + retValCollection.add(new Include(value, recurse)); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index 9c6fc1e76de..87b648840bf 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -192,7 +192,7 @@ public class MethodUtil { b.append(theMethod.getName()); b.append("' is annotated with @"); b.append(ResourceParam.class.getSimpleName()); - b.append(" but has a type that is not an implemtation of "); + b.append(" but has a type that is not an implementation of "); b.append(IBaseResource.class.getCanonicalName()); b.append(" or String or byte[]"); throw new ConfigurationException(b.toString()); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java index cb518bb8d57..ee71f6e210b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java @@ -20,23 +20,22 @@ package ca.uhn.fhir.rest.server.servlet; * #L% */ -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.util.List; -import java.util.Map.Entry; -import java.util.zip.GZIPOutputStream; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; - -import org.hl7.fhir.instance.model.api.IBaseBinary; - import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.server.ParseAction; import ca.uhn.fhir.rest.server.RestfulResponse; +import org.hl7.fhir.instance.model.api.IBaseBinary; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map.Entry; +import java.util.zip.GZIPOutputStream; public class ServletRestfulResponse extends RestfulResponse { @@ -48,23 +47,23 @@ public class ServletRestfulResponse extends RestfulResponse createServerConformanceProvider(RestfulServer theRestfulServer) { + return new IServerConformanceProvider() { + + @Override + @Metadata + public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { + return mock(IBaseConformance.class); + } + + @Override + public void setRestfulServer(RestfulServer theRestfulServer) { + } + }; + } + + @Override + public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) { + return new MyResourceProvider(); + } + } + + @SuppressWarnings("unused") + private static class MyResourceProvider implements IResourceProvider { + @Create + public MethodOutcome create(@ResourceParam IBaseResource theResource) { + return mock(MethodOutcome.class); + } + + @Override + public Class getResourceType() { + return IBaseResource.class; + } + } + +} diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index e6f6a6546cd..834ad669a7d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 6460fdaa090..31805755c96 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index ec9c4c7b647..1d4b77cb9f0 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 45d11f2b0a0..09556aa2e9d 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml index 2f99de20e6b..a087c508c21 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-sample-server-jpa diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index bdeeb2a13a3..5b9868d5d9c 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 95bbb629d7f..4869dd92e80 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 83dcdd267d7..1b15296e40b 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index c12b498af04..2e2a8777742 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java index 315736213f7..b611feb0055 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java @@ -1170,44 +1170,6 @@ public class JsonParserDstu2_1Test { assertEquals("", out); } - // FIXME: this should pass - @Test - @Ignore - public void testNamespacePreservationEncode() throws Exception { - //@formatter:off - String input = "" + - "" + - "" + - "" + - "@fhirabend" + - "" + - "" + - ""; - //@formatter:on - Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, input); - - String expected = "@fhirabend"; - assertEquals(expected, parsed.getText().getDiv().getValueAsString()); - - String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed); - ourLog.info(encoded); - assertThat(encoded, containsString("\"div\":\"" + expected.replace("\"", "\\\"") + "\"")); - } - - // TODO: this should pass - @Test - @Ignore - public void testNamespacePreservationParse() throws Exception { - String input = "{\"resourceType\":\"Patient\",\"text\":{\"div\":\"@fhirabend\"}}"; - Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, input); - XhtmlNode div = parsed.getText().getDiv(); - - assertEquals("@fhirabend", div.getValueAsString()); - - String encoded = ourCtx.newXmlParser().encodeResourceToString(parsed); - assertEquals("@fhirabend", encoded); - } - @Test public void testOmitResourceId() { Patient p = new Patient(); diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index cb64e29ef8d..48444b154a0 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java index 66648cd8afe..d67c4468f42 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java @@ -41,9 +41,6 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -/** - * Created by dsotnikov on 2/25/2014. - */ public class IncludeDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeDstu2Test.class); @@ -310,9 +307,6 @@ public class IncludeDstu2Test { } - /** - * Created by dsotnikov on 2/25/2014. - */ public static class DummyPatientResourceProvider implements IResourceProvider { @Search(queryName = "containedInclude") diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index e8955655dd3..f8d1d20457a 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java index cf67693767e..84e62466d92 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java @@ -106,14 +106,22 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander @Override public T fetchResource(Class theClass, String theUri) { + Validate.notBlank(theUri, "theUri must not be null or blank"); if (myValidationSupport == null) { return null; } else { - @SuppressWarnings("unchecked") - T retVal = (T) myFetchedResourceCache.get(theUri, t->{ - return myValidationSupport.fetchResource(myCtx, theClass, theUri); - }); - return retVal; + try { + //noinspection unchecked + return (T) myFetchedResourceCache.get(theUri, t -> { + T resource = myValidationSupport.fetchResource(myCtx, theClass, theUri); + if (resource == null) { + throw new IllegalArgumentException(); + } + return resource; + }); + } catch (IllegalArgumentException e) { + return null; + } } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java index 3211b621a89..3b58f877a14 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java @@ -1593,44 +1593,6 @@ public class JsonParserDstu3Test { assertEquals("", out); } - // FIXME: this should pass - @Test - @Ignore - public void testNamespacePreservationEncode() { - //@formatter:off - String input = "" + - "" + - "" + - "" + - "@fhirabend" + - "" + - "" + - ""; - //@formatter:on - Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, input); - - String expected = "@fhirabend"; - assertEquals(expected, parsed.getText().getDiv().getValueAsString()); - - String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed); - ourLog.info(encoded); - assertThat(encoded, containsString("\"div\":\"" + expected.replace("\"", "\\\"") + "\"")); - } - - // TODO: this should pass - @Test - @Ignore - public void testNamespacePreservationParse() { - String input = "{\"resourceType\":\"Patient\",\"text\":{\"div\":\"@fhirabend\"}}"; - Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, input); - XhtmlNode div = parsed.getText().getDiv(); - - assertEquals("@fhirabend", div.getValueAsString()); - - String encoded = ourCtx.newXmlParser().encodeResourceToString(parsed); - assertEquals("@fhirabend", encoded); - } - @Test public void testOmitResourceId() { Patient p = new Patient(); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java index 625b0ba0a85..40bb7342a37 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java @@ -1538,21 +1538,21 @@ public class GenericClientDstu3Test { IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); int idx = 0; - client - .search() - .forResource(Patient.class) - .sort().ascending("address") - .returnBundle(Bundle.class) - .execute(); - assertEquals("http://example.com/fhir/Patient?_sort=address", capt.getAllValues().get(idx++).getURI().toASCIIString()); - - client - .search() - .forResource(Patient.class) - .sort().descending("address") - .returnBundle(Bundle.class) - .execute(); - assertEquals("http://example.com/fhir/Patient?_sort=-address", capt.getAllValues().get(idx++).getURI().toASCIIString()); +// client +// .search() +// .forResource(Patient.class) +// .sort().ascending("address") +// .returnBundle(Bundle.class) +// .execute(); +// assertEquals("http://example.com/fhir/Patient?_sort=address", capt.getAllValues().get(idx++).getURI().toASCIIString()); +// +// client +// .search() +// .forResource(Patient.class) +// .sort().descending("address") +// .returnBundle(Bundle.class) +// .execute(); +// assertEquals("http://example.com/fhir/Patient?_sort=-address", capt.getAllValues().get(idx++).getURI().toASCIIString()); client .search() diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index f92fd6871e2..ab9eb4e3214 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index e4e51da7cfa..cdea8e99b14 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java index 64904f62529..5527e7602b6 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java @@ -10,6 +10,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullWriter; import org.apache.commons.lang.StringUtils; import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.junit.AfterClass; import org.junit.Ignore; import org.junit.Test; @@ -52,6 +53,38 @@ public class JsonParserR4Test extends BaseTest { ourLog.info(narrative); } + @Test + public void testNamespacePrefixTrimmedFromNarrative() { + String input = "" + + "" + + "" + + "" + + "@fhirabend" + + "" + + "" + + ""; + Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, input); + + String expected = "
@fhirabend
"; + assertEquals(expected, parsed.getText().getDiv().getValueAsString()); + + String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed); + ourLog.info(encoded); + assertThat(encoded, containsString("\"div\":\"" + expected.replace("\"", "\\\"") + "\"")); + } + + @Test + public void testNamespacePrefixStrippedOnJsonParse() { + String input = "{\"resourceType\":\"Patient\",\"text\":{\"div\":\"@fhirabend\"}}"; + Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, input); + XhtmlNode div = parsed.getText().getDiv(); + + assertEquals("
@fhirabend
", div.getValueAsString()); + + String encoded = ourCtx.newXmlParser().encodeResourceToString(parsed); + assertEquals("
@fhirabend
", encoded); + } + @Test public void testEncodeExtensionOnBinaryData() { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java index 6336aeca665..70c27a03c0d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientR4Test.java @@ -4,11 +4,14 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.annotation.ResourceDef; +import ca.uhn.fhir.rest.annotation.At; +import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Elements; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; @@ -18,6 +21,7 @@ import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest; import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; @@ -36,6 +40,7 @@ import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; @@ -228,6 +233,80 @@ public class ClientR4Test { assertEquals("200", response.getId().getVersionIdPart()); } + interface MyClient extends IRestfulClient { + + @Search() + List search(@IncludeParam String theInclude); + + + } + + + @Test + public void testStringIncludeTest() throws Exception { + + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.SEARCHSET); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ourCtx.newXmlParser().encodeResourceToString(bundle)), StandardCharsets.UTF_8)); + when(myHttpResponse.getAllHeaders()).thenReturn(new Header[0]); + + MyClient client = ourCtx.newRestfulClient(MyClient.class, "http://foo"); + List response = client.search("Patient:organization"); + + assertEquals(HttpGet.class, capt.getValue().getClass()); + HttpGet post = (HttpGet) capt.getValue(); + assertEquals("http://foo/Patient?_include=Patient%3Aorganization", post.getURI().toString()); + } + + @Test + public void testCreateWithInvalidType() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:foo").setValue("123"); + String serialized = ourCtx.newXmlParser().encodeResourceToString(patient); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(serialized), StandardCharsets.UTF_8)); + when(myHttpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200")); + + try { + ourCtx.newRestfulClient(ITestClientWithCreateWithInvalidParameterType.class, "http://foo"); + fail(); + } catch (ConfigurationException e) { + assertEquals("Method 'createPatient' is annotated with @ResourceParam but has a type that is not an implementation of org.hl7.fhir.instance.model.api.IBaseResource", e.getMessage()); + } + } + + @Test + public void testCreateWithValidAndInvalidType() throws Exception { + + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:foo").setValue("123"); + String serialized = ourCtx.newXmlParser().encodeResourceToString(patient); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(serialized), StandardCharsets.UTF_8)); + when(myHttpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200")); + + try { + ourCtx.newRestfulClient(ITestClientWithCreateWithValidAndInvalidParameterType.class, "http://foo"); + fail(); + } catch (ConfigurationException e) { + assertEquals("Parameter #2/2 of method 'createPatient' on type 'ca.uhn.fhir.rest.client.ClientR4Test.ITestClientWithCreateWithValidAndInvalidParameterType' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter", e.getMessage()); + } + } + @Test public void testDelete() throws Exception { @@ -946,6 +1025,45 @@ public class ClientR4Test { } + @Test + public void testSearchWithAt() throws Exception { + + String msg = getPatientFeedWithOneResult(); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8)); + + ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo"); + client.getPatientWithAt(new InstantType("2010-10-01T01:02:03.0Z")); + + assertEquals("http://foo/Patient?_at=2010-10-01T01%3A02%3A03.0Z", capt.getValue().getURI().toString()); + + } + + @Test + public void testUnannotatedMethod() throws Exception { + + String msg = getPatientFeedWithOneResult(); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8)); + + ITestClientWithUnannotatedMethod client = ourCtx.newRestfulClient(ITestClientWithUnannotatedMethod.class, "http://foo"); + try { + client.getPatientWithAt(new InstantType("2010-10-01T01:02:03.0Z")); + fail(); + } catch (UnsupportedOperationException e) { + assertEquals("The method 'getPatientWithAt' in type ITestClientWithUnannotatedMethod has no handler. Did you forget to annotate it with a RESTful method annotation?", e.getMessage()); + } + + } + @Test public void testSearchWithOptionalParam() throws Exception { @@ -978,7 +1096,6 @@ public class ClientR4Test { } - @Test public void testSearchWithStringIncludes() throws Exception { @@ -1183,7 +1300,6 @@ public class ClientR4Test { } - @Test public void testValidateOutcomeResponse() throws Exception { @@ -1213,7 +1329,6 @@ public class ClientR4Test { assertNull(response.getResource()); } - @Test public void testVRead() throws Exception { @@ -1324,6 +1439,18 @@ public class ClientR4Test { } } + public interface ITestClientWithCreateWithInvalidParameterType extends IRestfulClient { + + @Create() + MethodOutcome createPatient(@ResourceParam int thePatient); + } + + public interface ITestClientWithCreateWithValidAndInvalidParameterType extends IRestfulClient { + + @Create() + MethodOutcome createPatient(@ResourceParam Patient thePatient, int theInt); + } + interface ITestClientWithAndOr extends IBasicClient { @Search() @@ -1382,6 +1509,10 @@ public class ClientR4Test { } + interface ITestClientWithUnannotatedMethod extends IRestfulClient { + void getPatientWithAt(@At InstantType theInstantType); + } + @ResourceDef(name = "Patient") public static class CustomPatient extends Patient { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java index 15f2d88c9c0..370f8e663fe 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java @@ -17,40 +17,40 @@ import ca.uhn.fhir.rest.param.*; public interface ITestClient extends IBasicClient { @Create - public MethodOutcome createPatient(@ResourceParam Patient thePatient); + MethodOutcome createPatient(@ResourceParam Patient thePatient); @Search() - public List getPatientByDateRange(@RequiredParam(name = "dateRange") DateRangeParam theIdentifiers); + List getPatientByDateRange(@RequiredParam(name = "dateRange") DateRangeParam theIdentifiers); @Search(type=Observation.class) - public Bundle getObservationByNameValueDate(@RequiredParam(name = Observation.SP_CODE_VALUE_DATE, compositeTypes= {StringParam.class,DateParam.class}) CompositeParam theIdentifiers); + Bundle getObservationByNameValueDate(@RequiredParam(name = Observation.SP_CODE_VALUE_DATE, compositeTypes = {StringParam.class, DateParam.class}) CompositeParam theIdentifiers); @Search() - public List getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate); + List getPatientByDob(@RequiredParam(name = Patient.SP_BIRTHDATE) DateParam theBirthDate); @Search(type=ExtendedPatient.class) - public List getPatientByDobWithGenericResourceReturnType(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate); + List getPatientByDobWithGenericResourceReturnType(@RequiredParam(name = Patient.SP_BIRTHDATE) DateParam theBirthDate); @Search(type=ExtendedPatient.class) - public List getPatientByDobWithGenericResourceReturnType2(@RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate); + List getPatientByDobWithGenericResourceReturnType2(@RequiredParam(name = Patient.SP_BIRTHDATE) DateParam theBirthDate); @Search() - public List getPatientMultipleIdentifiers(@RequiredParam(name = "ids") TokenOrListParam theIdentifiers); + List getPatientMultipleIdentifiers(@RequiredParam(name = "ids") TokenOrListParam theIdentifiers); @Search(queryName="someQueryNoParams") - public Patient getPatientNoParams(); + Patient getPatientNoParams(); @Search(queryName="someQueryOneParam") - public Patient getPatientOneParam(@RequiredParam(name="param1") StringParam theParam); + Patient getPatientOneParam(@RequiredParam(name = "param1") StringParam theParam); @Search(type=Patient.class) - public Bundle findPatient(@RequiredParam(name = "param") StringAndListParam theStrings); + Bundle findPatient(@RequiredParam(name = "param") StringAndListParam theStrings); @Search() - public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringParam theString, @IncludeParam List theIncludes); + Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringParam theString, @IncludeParam List theIncludes); @Update - public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient); + MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient); @Delete(type=DiagnosticReport.class) void deleteDiagnosticReport(@IdParam IdType theId); @@ -89,7 +89,8 @@ public interface ITestClient extends IBasicClient { Patient findPatientQuantity(@RequiredParam(name="quantityParam") QuantityParam theQuantityType); @Search(compartmentName="compartmentName") - public List getPatientByCompartmentAndDob(@IdParam IdType theIdType, @RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theBirthDate); - + List getPatientByCompartmentAndDob(@IdParam IdType theIdType, @RequiredParam(name = Patient.SP_BIRTHDATE) DateParam theBirthDate); + @Search + Patient getPatientWithAt(@At InstantType theInstantType); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java index b99d57cf3c9..b477eb48cdc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java @@ -262,9 +262,21 @@ public class IncludeTest { } } - /** - * Created by dsotnikov on 2/25/2014. - */ + @Test + public void testStringInclude() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=stringInclude&_include=foo"); + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { + String responseContent = IOUtils.toString(status.getEntity().getContent()); + + assertEquals(200, status.getStatusLine().getStatusCode()); + Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); + assertEquals(1, bundle.getEntry().size()); + + Patient p = BundleUtil.toListOfResourcesOfType(ourCtx, bundle, Patient.class).get(0); + assertEquals("foo", p.getIdentifierFirstRep().getValue()); + } + } + public static class DummyDiagnosticReportResourceProvider implements IResourceProvider { @Override @@ -392,6 +404,17 @@ public class IncludeTest { return retVal; } + + @Search(queryName = "stringInclude") + public List stringInclude(@IncludeParam String theInclude) { + Patient p = new Patient(); + p.setId("p"); + p.addIdentifier().setValue(theInclude); + + return Arrays.asList(p); + } + + @Override public Class getResourceType() { return Patient.class; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java index e2509542a19..3868584a9bc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java @@ -54,7 +54,7 @@ public class ServerInvalidDefinitionR4Test { } catch (ServletException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains - .containsString("Method 'update' is annotated with @ResourceParam but has a type that is not an implemtation of org.hl7.fhir.instance.model.api.IBaseResource or String or byte[]")); + .containsString("Method 'update' is annotated with @ResourceParam but has a type that is not an implementation of org.hl7.fhir.instance.model.api.IBaseResource or String or byte[]")); } } diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 92dabe9671b..ffd8da62120 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java index c48a81f8f5e..c88ada2d294 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java @@ -343,6 +343,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public StructureDefinition fetchRawProfile(String url) { + throw new UnsupportedOperationException(); + } + @Override public List getTypeNames() { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index d010e06b048..6caefd3117b 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -19,17 +19,17 @@ ca.uhn.hapi.fhir hapi-fhir-base - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-server - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-client - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index b8bca0e0dad..107e75d8313 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index ad05fd25d1a..c4bae7d6848 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 45aa002c077..d8b4b070e80 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 566d901943a..152a124fc68 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index e0284321e23..901d583f414 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index afa97d77f1c..708cbe421a7 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 751a3b721ee..9a7e1c13971 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java index d1c28f2b274..37323be9e1c 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/ValidatorWrapper.java @@ -4,20 +4,24 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.util.XmlUtil; import ca.uhn.fhir.validation.IValidationContext; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.apache.commons.codec.Charsets; import org.apache.commons.io.input.ReaderInputStream; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager; +import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.ValidationProfileSet; import org.hl7.fhir.r5.validation.InstanceValidator; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.io.InputStream; @@ -33,7 +37,9 @@ public class ValidatorWrapper { private boolean myAnyExtensionsAllowed; private boolean myErrorForUnknownProfiles; private boolean myNoTerminologyChecks; + private boolean myAssumeValidRestReferences; private Collection myExtensionDomains; + private IResourceValidator.IValidatorResourceFetcher myValidatorResourceFetcher; /** * Constructor @@ -42,6 +48,15 @@ public class ValidatorWrapper { super(); } + public boolean isAssumeValidRestReferences() { + return myAssumeValidRestReferences; + } + + public ValidatorWrapper setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.myAssumeValidRestReferences = assumeValidRestReferences; + return this; + } + public ValidatorWrapper setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) { myBestPracticeWarningLevel = theBestPracticeWarningLevel; return this; @@ -67,6 +82,12 @@ public class ValidatorWrapper { return this; } + + public ValidatorWrapper setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.myValidatorResourceFetcher = validatorResourceFetcher; + return this; + } + public List validate(IWorkerContext theWorkerContext, IValidationContext theValidationContext) { InstanceValidator v; FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator.NullEvaluationContext(); @@ -76,19 +97,21 @@ public class ValidatorWrapper { throw new ConfigurationException(e); } + v.setAssumeValidRestReferences(isAssumeValidRestReferences()); v.setBestPracticeWarningLevel(myBestPracticeWarningLevel); v.setAnyExtensionsAllowed(myAnyExtensionsAllowed); v.setResourceIdRule(IResourceValidator.IdStatus.OPTIONAL); v.setNoTerminologyChecks(myNoTerminologyChecks); v.setErrorForUnknownProfiles(myErrorForUnknownProfiles); v.getExtensionDomains().addAll(myExtensionDomains); + v.setFetcher(myValidatorResourceFetcher); v.setAllowXsiLocation(true); List messages = new ArrayList<>(); - ValidationProfileSet profileSet = new ValidationProfileSet(); + List profileUrls = new ArrayList<>(); for (String next : theValidationContext.getOptions().getProfiles()) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(next, true)); + fetchAndAddProfile(theWorkerContext, profileUrls, next); } String input = theValidationContext.getResourceAsString(); @@ -109,14 +132,14 @@ public class ValidatorWrapper { // Determine if meta/profiles are present... ArrayList profiles = determineIfProfilesSpecified(document); for (String nextProfile : profiles) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(nextProfile, true)); + fetchAndAddProfile(theWorkerContext, profileUrls, nextProfile); } String resourceAsString = theValidationContext.getResourceAsString(); InputStream inputStream = new ReaderInputStream(new StringReader(resourceAsString), Charsets.UTF_8); Manager.FhirFormat format = Manager.FhirFormat.XML; - v.validate(null, messages, inputStream, format, profileSet); + v.validate(null, messages, inputStream, format, profileUrls); } else if (encoding == EncodingEnum.JSON) { @@ -129,7 +152,8 @@ public class ValidatorWrapper { if (profileElement != null && profileElement.isJsonArray()) { JsonArray profiles = profileElement.getAsJsonArray(); for (JsonElement element : profiles) { - profileSet.getCanonical().add(new ValidationProfileSet.ProfileRegistration(element.getAsString(), true)); + String nextProfile = element.getAsString(); + fetchAndAddProfile(theWorkerContext, profileUrls, nextProfile); } } } @@ -138,7 +162,7 @@ public class ValidatorWrapper { InputStream inputStream = new ReaderInputStream(new StringReader(resourceAsString), Charsets.UTF_8); Manager.FhirFormat format = Manager.FhirFormat.JSON; - v.validate(null, messages, inputStream, format, profileSet); + v.validate(null, messages, inputStream, format, profileUrls); } else { throw new IllegalArgumentException("Unknown encoding: " + encoding); @@ -157,17 +181,16 @@ public class ValidatorWrapper { return messages; } - - private String determineResourceName(Document theDocument) { - NodeList list = theDocument.getChildNodes(); - for (int i = 0; i < list.getLength(); i++) { - if (list.item(i) instanceof Element) { - return list.item(i).getLocalName(); - } + private void fetchAndAddProfile(IWorkerContext theWorkerContext, List theProfileStructureDefinitions, String theUrl) throws org.hl7.fhir.exceptions.FHIRException { + try { + StructureDefinition structureDefinition = theWorkerContext.fetchResourceWithException(StructureDefinition.class, theUrl); + theProfileStructureDefinitions.add(structureDefinition); + } catch (FHIRException e) { + ourLog.debug("Failed to load profile: {}", theUrl); } - return theDocument.getDocumentElement().getLocalName(); } + private ArrayList determineIfProfilesSpecified(Document theDocument) { ArrayList profileNames = new ArrayList<>(); NodeList list = theDocument.getChildNodes().item(0).getChildNodes(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java index e583381a5ee..e4ab01d7f78 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/FhirInstanceValidator.java @@ -1,14 +1,10 @@ package org.hl7.fhir.dstu2016may.hapi.validation; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.validation.IValidationContext; +import ca.uhn.fhir.validation.IValidatorModule; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import org.apache.commons.lang3.Validate; @@ -18,28 +14,45 @@ import org.apache.commons.lang3.time.DateUtils; import org.fhir.ucum.UcumService; import org.hl7.fhir.common.hapi.validation.ValidatorWrapper; import org.hl7.fhir.convertors.VersionConvertor_14_50; +import org.hl7.fhir.convertors.conv14_50.CodeSystem14_50; +import org.hl7.fhir.convertors.conv14_50.StructureDefinition14_50; +import org.hl7.fhir.convertors.conv14_50.ValueSet14_50; +import org.hl7.fhir.dstu2016may.model.CodeSystem; +import org.hl7.fhir.dstu2016may.model.CodeableConcept; +import org.hl7.fhir.dstu2016may.model.Coding; +import org.hl7.fhir.dstu2016may.model.ImplementationGuide; +import org.hl7.fhir.dstu2016may.model.Questionnaire; +import org.hl7.fhir.dstu2016may.model.StructureDefinition; +import org.hl7.fhir.dstu2016may.model.ValueSet; import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.ParserType; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.utils.INarrativeGenerator; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.dstu2016may.model.*; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationOptions; -import org.w3c.dom.*; +import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.validation.IValidationContext; -import ca.uhn.fhir.validation.IValidatorModule; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { @@ -382,7 +395,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid } public org.hl7.fhir.r5.model.StructureDefinition convert(StructureDefinition next) { - org.hl7.fhir.r5.model.StructureDefinition structureDefinition = VersionConvertor_14_50.convertStructureDefinition(next); + org.hl7.fhir.r5.model.StructureDefinition structureDefinition = StructureDefinition14_50.convertStructureDefinition(next); if (next.getDerivation() != org.hl7.fhir.dstu2016may.model.StructureDefinition.TypeDerivationRule.CONSTRAINT) { structureDefinition.setType(next.getName()); } @@ -408,7 +421,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = VersionConvertor_14_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = CodeSystem14_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -428,7 +441,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid public org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = VersionConvertor_14_50.convertValueSet(source); + convertedSource = ValueSet14_50.convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -437,7 +450,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = VersionConvertor_14_50.convertValueSet(expanded.getValueset()); + convertedResult = ValueSet14_50.convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -458,7 +471,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = VersionConvertor_14_50.convertConceptSetComponent(inc); + convertedInc = ValueSet14_50.convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -468,7 +481,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = VersionConvertor_14_50.convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = ValueSet14_50.convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -492,7 +505,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid return null; } try { - return VersionConvertor_14_50.convertCodeSystem(fetched); + return CodeSystem14_50.convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -572,6 +585,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { @@ -665,7 +683,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid try { if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -680,7 +698,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -700,7 +718,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid convertedCode = VersionConvertor_14_50.convertCoding(code); } if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -720,7 +738,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid convertedCode = VersionConvertor_14_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = VersionConvertor_14_50.convertValueSet(vs); + convertedVs = ValueSet14_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java index ba3c7a8755f..609941ba6a9 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.java @@ -23,6 +23,7 @@ import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.ImplementationGuide; import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.dstu3.model.SearchParameter; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.exceptions.FHIRException; @@ -56,6 +57,13 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import static org.hl7.fhir.convertors.conv30_50.CodeSystem30_50.convertCodeSystem; +import static org.hl7.fhir.convertors.conv30_50.CodeSystem30_50.convertConceptDefinitionComponent; +import static org.hl7.fhir.convertors.conv30_50.StructureDefinition30_50.convertStructureDefinition; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertConceptSetComponent; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertValueSet; +import static org.hl7.fhir.convertors.conv30_50.ValueSet30_50.convertValueSetExpansionComponent; + @SuppressWarnings({"PackageAccessibility", "Duplicates"}) public class FhirInstanceValidator extends BaseValidatorBridge implements IInstanceValidatorModule { @@ -66,10 +74,12 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta private StructureDefinition myStructureDefintion; private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; private volatile WorkerContextWrapper myWrappedWorkerContext; private boolean errorForUnknownProfiles; private List myExtensionDomains = Collections.emptyList(); + private boolean assumeValidRestReferences; /** * Constructor @@ -285,10 +295,28 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -327,6 +355,9 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta case "ImplementationGuide": fetched = myWrap.fetchResource(ImplementationGuide.class, key.getUri()); break; + case "SearchParameter": + fetched = myWrap.fetchResource(SearchParameter.class, key.getUri()); + break; default: throw new UnsupportedOperationException("Don't know how to fetch " + key.getResourceName()); } @@ -386,7 +417,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - retVal.add(VersionConvertor_30_50.convertStructureDefinition(next)); + retVal.add(convertStructureDefinition(next)); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -416,7 +447,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = VersionConvertor_30_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -436,7 +467,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = VersionConvertor_30_50.convertValueSet(source); + convertedSource = convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -445,7 +476,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = VersionConvertor_30_50.convertValueSet(expanded.getValueset()); + convertedResult = convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -467,7 +498,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = VersionConvertor_30_50.convertConceptSetComponent(inc); + convertedInc = convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -477,7 +508,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = VersionConvertor_30_50.convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -495,7 +526,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return null; } try { - return VersionConvertor_30_50.convertCodeSystem(fetched); + return convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -584,6 +615,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -676,7 +712,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -691,7 +727,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -711,7 +747,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta convertedCode = VersionConvertor_30_50.convertCoding(code); } if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -731,7 +767,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta convertedCode = VersionConvertor_30_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = VersionConvertor_30_50.convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java index a92e2a957af..877c9cfa880 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/FhirInstanceValidator.java @@ -27,12 +27,13 @@ import org.fhir.ucum.UcumService; import org.hl7.fhir.converter.NullVersionConverterAdvisor50; import org.hl7.fhir.convertors.VersionConvertorAdvisor50; import org.hl7.fhir.convertors.VersionConvertor_10_50; +import org.hl7.fhir.convertors.conv10_50.ValueSet10_50; +import org.hl7.fhir.convertors.conv14_50.CodeSystem14_50; import org.hl7.fhir.dstu2.model.CodeableConcept; import org.hl7.fhir.dstu2.model.Coding; import org.hl7.fhir.dstu2.model.Questionnaire; import org.hl7.fhir.dstu2.model.StructureDefinition; import org.hl7.fhir.dstu2.model.ValueSet; -import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.r5.context.IWorkerContext; @@ -60,6 +61,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -69,6 +71,11 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.hl7.fhir.convertors.VersionConvertor_10_50.convertCoding; +import static org.hl7.fhir.convertors.conv10_50.StructureDefinition10_50.convertStructureDefinition; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertConceptSetComponent; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertValueSet; +import static org.hl7.fhir.convertors.conv10_50.ValueSet10_50.convertValueSetExpansionComponent; public class FhirInstanceValidator extends BaseValidatorBridge implements IInstanceValidatorModule { @@ -81,8 +88,19 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta private StructureDefinition myStructureDefintion; private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + + private boolean assumeValidRestReferences; private volatile WorkerContextWrapper myWrappedWorkerContext; private VersionConvertorAdvisor50 myAdvisor = new NullVersionConverterAdvisor50(); + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; /** * Constructor @@ -287,6 +305,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setResourceIdRule(IdStatus.OPTIONAL); v.setNoTerminologyChecks(isNoTerminologyChecks()); + v.setFetcher(getValidatorResourceFetcher()); + v.setAssumeValidRestReferences(isAssumeValidRestReferences()); List messages = new ArrayList<>(); @@ -388,6 +408,14 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return messages; } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + @Override protected List validate(IValidationContext theCtx) { return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding()); @@ -428,7 +456,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return null; } } - profileText = IOUtils.toString(inputStream, "UTF-8"); + profileText = IOUtils.toString(inputStream, StandardCharsets.UTF_8); } catch (IOException e1) { if (theMessages != null) { theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL) @@ -457,11 +485,17 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta case "StructureDefinition": fetched = myWrap.fetchResource(StructureDefinition.class, key.getUri()); break; + case "CodeSystem": case "ValueSet": fetched = myWrap.fetchResource(ValueSet.class, key.getUri()); - break; - case "CodeSystem": - fetched = myWrap.fetchResource(ValueSet.class, key.getUri()); + + ValueSet fetchedVs = (ValueSet) fetched; + if (!fetchedVs.hasCompose()) { + if (fetchedVs.hasCodeSystem()) { + fetchedVs.getCompose().addInclude().setSystem(fetchedVs.getCodeSystem().getSystem()); + } + } + break; case "Questionnaire": fetched = myWrap.fetchResource(Questionnaire.class, key.getUri()); @@ -477,7 +511,15 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta } try { - org.hl7.fhir.r5.model.Resource converted = new VersionConvertor_10_50(myAdvisor).convertResource(fetched); + org.hl7.fhir.r5.model.Resource converted; + if ("CodeSystem".equals(key.getUri())) { + NullVersionConverterAdvisor50 advisor = new NullVersionConverterAdvisor50(); + converted = ValueSet10_50.convertValueSet((ValueSet) fetched, advisor); + converted = advisor.getCodeSystem((org.hl7.fhir.r5.model.ValueSet) converted); + } else { + converted = VersionConvertor_10_50.convertResource(fetched); + } + if (fetched instanceof StructureDefinition) { StructureDefinition fetchedSd = (StructureDefinition) fetched; @@ -501,7 +543,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public WorkerContextWrapper(HapiWorkerContext theWorkerContext) { myWrap = theWorkerContext; - myConverter = new VersionConvertor_10_50(myAdvisor); + myConverter = new VersionConvertor_10_50(); } @Override @@ -510,7 +552,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta } @Override - public void generateSnapshot(org.hl7.fhir.r5.model.StructureDefinition p) throws DefinitionException, FHIRException { + public void generateSnapshot(org.hl7.fhir.r5.model.StructureDefinition p) throws FHIRException { } @@ -547,7 +589,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - org.hl7.fhir.r5.model.StructureDefinition converted = new VersionConvertor_10_50(myAdvisor).convertStructureDefinition(next); + org.hl7.fhir.r5.model.StructureDefinition converted = convertStructureDefinition(next); if (converted != null) { retVal.add(converted); } @@ -587,7 +629,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource = null; try { - convertedSource = new VersionConvertor_10_50(myAdvisor).convertValueSet(source); + convertedSource = convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -596,7 +638,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = new VersionConvertor_10_50(myAdvisor).convertValueSet(expanded.getValueset()); + convertedResult = convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -618,7 +660,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = new VersionConvertor_10_50(myAdvisor).convertConceptSetComponent(inc); + convertedInc = convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -628,7 +670,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent valueSetExpansionComponent = null; if (expansion != null) { try { - valueSetExpansionComponent = new VersionConvertor_10_50(myAdvisor).convertValueSetExpansionComponent(expansion); + valueSetExpansionComponent = convertValueSetExpansionComponent(expansion); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -733,6 +775,11 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public void setUcumService(UcumService ucumService) { throw new UnsupportedOperationException(); @@ -825,7 +872,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -840,8 +887,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta ValueSet convertedVs = null; try { if (vs != null) { - VersionConvertorAdvisor50 advisor50 = new NullVersionConverterAdvisor50(); - convertedVs = new VersionConvertor_10_50(advisor50).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -858,10 +904,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (code != null) { - convertedCode = new VersionConvertor_10_50(myAdvisor).convertCoding(code); + convertedCode = convertCoding(code); } if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -878,10 +924,10 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta try { if (code != null) { - convertedCode = new VersionConvertor_10_50(myAdvisor).convertCodeableConcept(code); + convertedCode = VersionConvertor_10_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = new VersionConvertor_10_50(myAdvisor).convertValueSet(vs); + convertedVs = convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java index 9cdae210cd8..1788fd582a5 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/instance/hapi/validation/HapiWorkerContext.java @@ -199,7 +199,7 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander nextSystem = nextComposeConceptSet.getSystem(); } - if (StringUtils.equals(nextSystem, nextComposeConceptSet.getSystem())) { + if (Constants.codeSystemNotNeeded(theSystem) || StringUtils.equals(nextSystem, nextComposeConceptSet.getSystem())) { for (ConceptReferenceComponent nextComposeCode : nextComposeConceptSet.getConcept()) { ConceptDefinitionComponent conceptDef = new ConceptDefinitionComponent(); conceptDef.setCode(nextComposeCode.getCode()); @@ -211,7 +211,13 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander } if (nextComposeConceptSet.getConcept().isEmpty()){ - ValidationResult result = validateCode(nextSystem, theCode, null); + + String validateSystem = nextSystem; + if (Constants.codeSystemNotNeeded(nextSystem)) { + validateSystem = nextComposeConceptSet.getSystem(); + } + + ValidationResult result = validateCode(validateSystem, theCode, null); if (result.isOk()){ return result; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java index d7c52d229a1..d5441294501 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/FhirInstanceValidator.java @@ -59,7 +59,8 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV private IValidationSupport myValidationSupport; private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; - + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; + private boolean assumeValidRestReferences; private boolean errorForUnknownProfiles; private List extensionDomains = Collections.emptyList(); @@ -225,13 +226,32 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + private List getExtensionDomains() { return extensionDomains; } + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -317,7 +337,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV retVal = new ArrayList<>(); for (StructureDefinition next : myWrap.allStructures()) { try { - retVal.add(org.hl7.fhir.convertors.conv40_50.StructureDefinition.convertStructureDefinition(next)); + retVal.add(org.hl7.fhir.convertors.conv40_50.StructureDefinition40_50.convertStructureDefinition(next)); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -347,7 +367,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null; if (theResult.asConceptDefinition() != null) { try { - conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertConceptDefinitionComponent(theResult.asConceptDefinition()); + conceptDefinition = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertConceptDefinitionComponent(theResult.asConceptDefinition()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -367,7 +387,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean heiarchical) { ValueSet convertedSource; try { - convertedSource = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(source); + convertedSource = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(source); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -376,7 +396,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.ValueSet convertedResult = null; if (expanded.getValueset() != null) { try { - convertedResult = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expanded.getValueset()); + convertedResult = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expanded.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -398,7 +418,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV ValueSet.ConceptSetComponent convertedInc = null; if (inc != null) { try { - convertedInc = org.hl7.fhir.convertors.conv40_50.ValueSet.convertConceptSetComponent(inc); + convertedInc = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertConceptSetComponent(inc); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -408,7 +428,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV org.hl7.fhir.r5.model.ValueSet valueSetExpansion = null; if (expansion != null) { try { - valueSetExpansion = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expansion.getValueset()); + valueSetExpansion = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(expansion.getValueset()); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -424,7 +444,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV return null; } try { - return org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(fetched); + return org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem(fetched); } catch (FHIRException e) { throw new InternalErrorException(e); } @@ -509,6 +529,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public org.hl7.fhir.r5.model.StructureDefinition fetchRawProfile(String url) { + return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -605,7 +630,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV try { if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -620,7 +645,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV ValueSet convertedVs = null; try { if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -640,7 +665,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV convertedCode = VersionConvertor_40_50.convertCoding(code); } if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); @@ -660,7 +685,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r4.hapi.validation.BaseV convertedCode = VersionConvertor_40_50.convertCodeableConcept(code); } if (vs != null) { - convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs); + convertedVs = org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(vs); } } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java index 02a3fd839d2..a6ced0b4654 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r5/hapi/validation/FhirInstanceValidator.java @@ -54,7 +54,9 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV private boolean noTerminologyChecks = false; private volatile WorkerContextWrapper myWrappedWorkerContext; private boolean errorForUnknownProfiles; + private boolean assumeValidRestReferences; private List myExtensionDomains = Collections.emptyList(); + private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher; /** * Constructor @@ -220,9 +222,27 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV .setErrorForUnknownProfiles(isErrorForUnknownProfiles()) .setExtensionDomains(getExtensionDomains()) .setNoTerminologyChecks(isNoTerminologyChecks()) + .setValidatorResourceFetcher(getValidatorResourceFetcher()) + .setAssumeValidRestReferences(isAssumeValidRestReferences()) .validate(wrappedWorkerContext, theValidationCtx); } + public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() { + return validatorResourceFetcher; + } + + public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) { + this.validatorResourceFetcher = validatorResourceFetcher; + } + + public boolean isAssumeValidRestReferences() { + return assumeValidRestReferences; + } + + public void setAssumeValidRestReferences(boolean assumeValidRestReferences) { + this.assumeValidRestReferences = assumeValidRestReferences; + } + private static class WorkerContextWrapper implements IWorkerContext { private final HapiWorkerContext myWrap; @@ -514,6 +534,11 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV return fetchResource(org.hl7.fhir.r5.model.StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public StructureDefinition fetchRawProfile(String url) { + return myWrap.fetchRawProfile(url); + } + @Override public List getTypeNames() { return myWrap.getTypeNames(); @@ -706,7 +731,7 @@ public class FhirInstanceValidator extends org.hl7.fhir.r5.hapi.validation.BaseV } @Override - public Base resolveReference(Object appContext, String url) throws FHIRException { + public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { return null; } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index bace1c7268e..29fbca55962 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -24,10 +24,12 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu3.utils.FHIRPathEngine; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.utils.IResourceValidator; import org.junit.*; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; +import org.mockito.internal.matchers.Any; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -41,8 +43,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class FhirInstanceValidatorDstu3Test { @@ -975,7 +976,7 @@ public class FhirInstanceValidatorDstu3Test { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -1145,6 +1146,20 @@ public class FhirInstanceValidatorDstu3Test { ourLog.info(output.getMessages().get(0).getMessage()); } + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(input); + + verify(resourceFetcher, times(3)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(4)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(4)).fetch(any(), anyString()); + } + @Test public void testValueWithWhitespace() throws IOException { String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index d4bbfac740e..7cbd9968f1e 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -704,7 +704,7 @@ public class QuestionnaireResponseValidatorDstu3Test { coding.setCode("1293"); QuestionnaireResponseItemAnswerComponent answer = qrItem.addAnswer(); answer.setValue(coding); - coding.addExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-hidden", new BooleanType(true)); + coding.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-CO-value", new DecimalType("1.0")); qr.addItem().setLinkId("2B").addAnswer().setValue(new BooleanType(true)); String reference = qr.getQuestionnaire().getReference(); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java index 4a8ececb99a..9e8a25f5a42 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; @@ -71,7 +72,7 @@ public class QuestionnaireValidatorDstu3Test { ValidationResult errors = myVal.validateWithResult(q); ourLog.info(errors.toString()); assertThat(errors.isSuccessful(), Matchers.is(true)); - assertThat(errors.getMessages(), Matchers.empty()); + assertThat(errors.getMessages().stream().filter(t->t.getSeverity().ordinal() > ResultSeverityEnum.INFORMATION.ordinal()).collect(Collectors.toList()), Matchers.empty()); } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index ce094c1a8a8..fe617e9b955 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -66,9 +66,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; public class FhirInstanceValidatorR4Test extends BaseTest { @@ -397,7 +398,11 @@ public class FhirInstanceValidatorR4Test extends BaseTest { ValidationResult output = myVal.validateWithResult(encoded); List errors = logResultsAndReturnNonInformationalOnes(output); - assertEquals(46, errors.size()); + errors = errors + .stream() + .filter(t->t.getMessage().contains("Bundle entry missing fullUrl")) + .collect(Collectors.toList()); + assertEquals(5, errors.size()); } @Test @@ -633,8 +638,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport)); // Prepopulate SDs - valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/dstu3/myconsent-profile.xml")); - valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/dstu3/myconsent-ext.xml")); + valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-profile.xml")); + valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-ext.xml")); FhirValidator val = ourCtx.newValidator(); val.registerValidatorModule(new FhirInstanceValidator(support)); @@ -652,6 +657,8 @@ public class FhirInstanceValidatorR4Test extends BaseTest { .setSystem("http://terminology.hl7.org/CodeSystem/consentcategorycodes") .setCode("acd"); + + // Should pass ValidationResult output = val.validateWithResult(input); List all = logResultsAndReturnErrorOnes(output); @@ -1031,7 +1038,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test @@ -1250,6 +1257,21 @@ public class FhirInstanceValidatorR4Test extends BaseTest { assertEquals(0, all.size()); } + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + + String encoded = loadResource("/r4/r4-caredove-bundle.json"); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(encoded); + + verify(resourceFetcher, times(14)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(12)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(12)).fetch(any(), anyString()); + } + @Test @Ignore public void testValidateStructureDefinition() throws IOException { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java index b6b88d44c15..ff872af31f4 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireValidatorR4Test.java @@ -23,6 +23,8 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.stream.Collectors; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; @@ -72,7 +74,7 @@ public class QuestionnaireValidatorR4Test { ValidationResult errors = myVal.validateWithResult(q); ourLog.info(errors.toString()); assertThat(errors.isSuccessful(), Matchers.is(true)); - assertThat(errors.getMessages(), Matchers.empty()); + assertThat(errors.getMessages().stream().filter(t->t.getSeverity().ordinal() > ResultSeverityEnum.INFORMATION.ordinal()).collect(Collectors.toList()), Matchers.empty()); } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java index c9f23538e15..05a8e54e3ff 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java @@ -53,9 +53,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; public class FhirInstanceValidatorR5Test { @@ -363,9 +364,7 @@ public class FhirInstanceValidatorR5Test { } - // FIXME: enable and change performed to occurrence @Test - @Ignore public void testCompareTimesWithDifferentTimezones() { Procedure procedure = new Procedure(); procedure.setStatus(Enumerations.EventStatus.COMPLETED); @@ -417,6 +416,23 @@ public class FhirInstanceValidatorR5Test { assertThat(nonInfo, empty()); } + + + @Test + public void testInvocationOfValidatorFetcher() throws IOException { + + String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/vitals.json"), Charsets.UTF_8); + + IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class); + when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + myInstanceVal.setValidatorResourceFetcher(resourceFetcher); + myVal.validateWithResult(input); + + verify(resourceFetcher, times(12)).resolveURL(any(), anyString(), anyString()); + verify(resourceFetcher, times(3)).validationPolicy(any(), anyString(), anyString()); + verify(resourceFetcher, times(3)).fetch(any(), anyString()); + } + @Test public void testIsNoTerminologyChecks() { assertFalse(myInstanceVal.isNoTerminologyChecks()); @@ -776,7 +792,7 @@ public class FhirInstanceValidatorR5Test { myInstanceVal.setValidationSupport(myMockSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString(), containsString("StructureDefinition reference \"http://foo/structuredefinition/myprofile\" could not be resolved")); + assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked")); } @Test diff --git a/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml b/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml new file mode 100644 index 00000000000..2049e440b22 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/r4/myconsent-ext.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml b/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml new file mode 100644 index 00000000000..0396797fe88 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/r4/myconsent-profile.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-validation/src/test/resources/vitals.json b/hapi-fhir-validation/src/test/resources/vitals.json new file mode 100644 index 00000000000..78b3e733c6b --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/vitals.json @@ -0,0 +1,97 @@ +{ + "resourceType": "Observation", + "id": "satO2", + "meta": { + "profile": [ + "http://hl7.org/fhir/StructureDefinition/vitalsigns" + ] + }, + "text": { + "status": "generated", + "div": "

Generated Narrative with Details

id: satO2

meta:

identifier: o1223435-10

partOf: Procedure/ob

status: final

category: Vital Signs (Details : {http://terminology.hl7.org/CodeSystem/observation-category code 'vital-signs' = 'Vital Signs', given as 'Vital Signs'})

code: Oxygen saturation in Arterial blood (Details : {LOINC code '2708-6' = 'Oxygen saturation in Arterial blood', given as 'Oxygen saturation in Arterial blood'}; {LOINC code '59408-5' = 'Oxygen saturation in Arterial blood by Pulse oximetry', given as 'Oxygen saturation in Arterial blood by Pulse oximetry'}; {urn:iso:std:iso:11073:10101 code '150456' = '150456', given as 'MDC_PULS_OXIM_SAT_O2'})

subject: Patient/example

effective: Dec 5, 2014 9:30:10 AM

value: 95 % (Details: UCUM code % = '%')

interpretation: Normal (applies to non-numeric results) (Details : {http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation code 'N' = 'Normal', given as 'Normal'})

device: DeviceMetric/example

ReferenceRanges

-LowHigh
*90 % (Details: UCUM code % = '%')99 % (Details: UCUM code % = '%')
" + }, + "identifier": [ + { + "system": "http://goodcare.org/observation/id", + "value": "o1223435-10" + } + ], + "partOf": [ + { + "reference": "Procedure/ob" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "vital-signs", + "display": "Vital Signs" + } + ], + "text": "Vital Signs" + } + ], + "code": { + "coding": [ + { + "system": "http://loinc.org", + "code": "2708-6", + "display": "Oxygen saturation in Arterial blood" + }, + { + "system": "http://loinc.org", + "code": "59408-5", + "display": "Oxygen saturation in Arterial blood by Pulse oximetry" + }, + { + "system": "urn:iso:std:iso:11073:10101", + "code": "150456", + "display": "MDC_PULS_OXIM_SAT_O2" + } + ] + }, + "subject": { + "reference": "Patient/example" + }, + "effectiveDateTime": "2014-12-05T09:30:10+01:00", + "valueQuantity": { + "value": 95, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "Normal" + } + ], + "text": "Normal (applies to non-numeric results)" + } + ], + "device": { + "reference": "DeviceMetric/example" + }, + "referenceRange": [ + { + "low": { + "value": 90, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + }, + "high": { + "value": 99, + "unit": "%", + "system": "http://unitsofmeasure.org", + "code": "%" + } + } + ] +} diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index ec1362ebd4c..4caaad9f81f 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 9e07aca1de0..55d2ca62a4f 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 890013750e2..ef8d1092440 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -590,6 +590,11 @@ dionmcm + + ttntrifork + Trifork + Tue Toft Nørgård + @@ -601,7 +606,7 @@ - 4.1.42-SNAPSHOT + 4.2.1-SNAPSHOT 1.0.2 -Dfile.encoding=UTF-8 -Xmx2048m @@ -1902,12 +1907,12 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.0.0 + 3.1.0 com.puppycrawl.tools checkstyle - 8.18 + 8.29 @@ -2555,6 +2560,30 @@ -Dfile.encoding=UTF-8 -Xmx2048m -XX:TieredStopAtLevel=1 -XX:+UseParallelGC -Xverify:none -Dfile.encoding=UTF-8 -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=2048M + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + validate + generate-sources + + src/checkstyle/checkstyle_config_nofixmes.xml + UTF-8 + true + true + false + + + check + + + + + + NOPARALLEL diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 69440e2adf4..4813953ee4a 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../pom.xml diff --git a/src/checkstyle/checkstyle_config_nofixmes.xml b/src/checkstyle/checkstyle_config_nofixmes.xml new file mode 100644 index 00000000000..2fd2fc95aad --- /dev/null +++ b/src/checkstyle/checkstyle_config_nofixmes.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index acc1aa4da1b..5c69d2bfd64 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index e2c2c0c28dc..f3a5689802a 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 2b0fddd66ed..56809aad658 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 4.2.0-SNAPSHOT + 4.3.0-SNAPSHOT ../../pom.xml