From 43ac912033f0c8d7d7b6b8bbd15320e02b630378 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 15 Jul 2018 15:22:52 -0400 Subject: [PATCH] Clean up terminology uploading --- .../java/ca/uhn/fhir/context/FhirContext.java | 409 +++++++++--------- .../src/main/resources/logback-cli-on.xml | 7 + .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 4 - .../fhir/jpa/dao/data/ITermConceptDao.java | 7 +- .../dao/data/ITermConceptDesignationDao.java | 3 + .../data/ITermConceptParentChildLinkDao.java | 7 +- .../jpa/dao/data/ITermConceptPropertyDao.java | 2 + .../dstu3/FhirResourceDaoValueSetDstu3.java | 7 + .../jpa/term/BaseHapiTerminologySvcImpl.java | 103 ++--- .../ResourceProviderDstu3ValueSetTest.java | 39 +- .../r4/ResourceProviderR4ValueSetTest.java | 85 +++- ...minologyLoaderSvcIntegrationDstu3Test.java | 38 +- .../resources/vm/jpa_spring_beans_java.vm | 15 +- 13 files changed, 421 insertions(+), 305 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 01ccc3ca7b8..6985f9e71a4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -1,7 +1,34 @@ package ca.uhn.fhir.context; +import ca.uhn.fhir.context.api.AddProfileTagEnum; +import ca.uhn.fhir.context.support.IContextValidationSupport; +import ca.uhn.fhir.fluentpath.IFluentPath; +import ca.uhn.fhir.i18n.HapiLocalizer; +import ca.uhn.fhir.model.api.IElement; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.view.ViewGenerator; +import ca.uhn.fhir.narrative.INarrativeGenerator; +import ca.uhn.fhir.parser.*; +import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; +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.IRestfulClientFactory; +import ca.uhn.fhir.util.FhirTerser; +import ca.uhn.fhir.util.ReflectionUtil; +import ca.uhn.fhir.util.VersionUtil; +import ca.uhn.fhir.validation.FhirValidator; +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; + import java.io.IOException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.Map.Entry; /* * #%L @@ -12,9 +39,9 @@ import java.lang.reflect.Method; * 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. @@ -23,30 +50,10 @@ import java.lang.reflect.Method; * #L% */ -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.Map.Entry; - -import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.*; - -import ca.uhn.fhir.context.api.AddProfileTagEnum; -import ca.uhn.fhir.context.support.IContextValidationSupport; -import ca.uhn.fhir.fluentpath.IFluentPath; -import ca.uhn.fhir.i18n.HapiLocalizer; -import ca.uhn.fhir.model.api.*; -import ca.uhn.fhir.model.view.ViewGenerator; -import ca.uhn.fhir.narrative.INarrativeGenerator; -import ca.uhn.fhir.parser.*; -import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; -import ca.uhn.fhir.rest.client.api.*; -import ca.uhn.fhir.util.*; -import ca.uhn.fhir.validation.FhirValidator; - /** * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then * used as a factory for various other types of objects (parsers, clients, etc.). - * + * *

* Important usage notes: *

@@ -68,6 +75,7 @@ public class FhirContext { private static final List> EMPTY_LIST = Collections.emptyList(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class); + private final IFhirVersion myVersion; private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM; private volatile Map, BaseRuntimeElementDefinition> myClassToElementDefinition = Collections.emptyMap(); private ArrayList> myCustomTypes; @@ -87,14 +95,11 @@ public class FhirContext { private volatile IRestfulClientFactory myRestfulClientFactory; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; private IContextValidationSupport myValidationSupport; - - private final IFhirVersion myVersion; - private Map>> myVersionToNameToResourceType = Collections.emptyMap(); /** * @deprecated It is recommended that you use one of the static initializer methods instead - * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} + * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} */ @Deprecated public FhirContext() { @@ -103,7 +108,7 @@ public class FhirContext { /** * @deprecated It is recommended that you use one of the static initializer methods instead - * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} + * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} */ @Deprecated public FhirContext(Class theResourceType) { @@ -112,7 +117,7 @@ public class FhirContext { /** * @deprecated It is recommended that you use one of the static initializer methods instead - * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} + * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} */ @Deprecated public FhirContext(Class... theResourceTypes) { @@ -121,7 +126,7 @@ public class FhirContext { /** * @deprecated It is recommended that you use one of the static initializer methods instead - * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} + * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} or {@link #forR4()} */ @Deprecated public FhirContext(Collection> theResourceTypes) { @@ -161,7 +166,7 @@ public class FhirContext { if (theVersion == null) { ourLog.info("Creating new FhirContext with auto-detected version [{}]. It is recommended to explicitly select a version for future compatibility by invoking FhirContext.forDstuX()", - myVersion.getVersion().name()); + myVersion.getVersion().name()); } else { ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name()); } @@ -201,13 +206,37 @@ public class FhirContext { * When encoding resources, this setting configures the parser to include * an entry in the resource's metadata section which indicates which profile(s) the * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. - * + * * @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information */ public AddProfileTagEnum getAddProfileTagWhenEncoding() { return myAddProfileTagWhenEncoding; } + /** + * When encoding resources, this setting configures the parser to include + * an entry in the resource's metadata section which indicates which profile(s) the + * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. + *

+ * This feature is intended for situations where custom resource types are being used, + * avoiding the need to manually add profile declarations for these custom types. + *

+ *

+ * See Profiling and Extensions + * for more information on using custom types. + *

+ *

+ * Note that this feature automatically adds the profile, but leaves any profile tags + * which have been manually added in place as well. + *

+ * + * @param theAddProfileTagWhenEncoding The add profile mode (must not be null) + */ + public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) { + Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null"); + myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding; + } + Collection getAllResourceDefinitions() { validateInitialized(); return myNameToResourceDefinition.values(); @@ -215,7 +244,7 @@ public class FhirContext { /** * Returns the default resource type for the given profile - * + * * @see #setDefaultTypeForProfile(String, Class) */ public Class getDefaultTypeForProfile(String theProfile) { @@ -249,7 +278,9 @@ public class FhirContext { return myNameToElementDefinition.get(theElementName.toLowerCase()); } - /** For unit tests only */ + /** + * For unit tests only + */ int getElementDefinitionCount() { validateInitialized(); return myClassToElementDefinition.size(); @@ -274,20 +305,43 @@ public class FhirContext { return myLocalizer; } + /** + * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with + * caution + */ + public void setLocalizer(HapiLocalizer theMessages) { + myLocalizer = theMessages; + } + public INarrativeGenerator getNarrativeGenerator() { return myNarrativeGenerator; } + public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) { + myNarrativeGenerator = theNarrativeGenerator; + } + /** * Returns the parser options object which will be used to supply default * options to newly created parsers - * + * * @return The parser options - Will not return null */ public ParserOptions getParserOptions() { return myParserOptions; } + /** + * Sets the parser options object which will be used to supply default + * options to newly created parsers + * + * @param theParserOptions The parser options object - Must not be null + */ + public void setParserOptions(ParserOptions theParserOptions) { + Validate.notNull(theParserOptions, "theParserOptions must not be null"); + myParserOptions = theParserOptions; + } + /** * Get the configured performance options */ @@ -295,6 +349,32 @@ public class FhirContext { return myPerformanceOptions; } + // /** + // * Return an unmodifiable collection containing all known resource definitions + // */ + // public Collection getResourceDefinitions() { + // + // Set> datatypes = Collections.emptySet(); + // Map, BaseRuntimeElementDefinition> existing = Collections.emptyMap(); + // HashMap> types = new HashMap>(); + // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); + // for (int next : types.) + // + // return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); + // } + + /** + * Sets the configured performance options + * + * @see PerformanceOptionsEnum for a list of available options + */ + public void setPerformanceOptions(Collection theOptions) { + myPerformanceOptions.clear(); + if (theOptions != null) { + myPerformanceOptions.addAll(theOptions); + } + } + /** * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed * for extending the core library. @@ -359,8 +439,12 @@ public class FhirContext { *

* Note that this method is case insensitive! *

+ * + * @throws DataFormatException If the resource name is not known */ - public RuntimeResourceDefinition getResourceDefinition(String theResourceName) { + // Multiple spots in HAPI FHIR and Smile CDR depend on DataFormatException being + // thrown by this method, don't change that. + public RuntimeResourceDefinition getResourceDefinition(String theResourceName) throws DataFormatException { validateInitialized(); Validate.notBlank(theResourceName, "theResourceName must not be blank"); @@ -380,20 +464,6 @@ public class FhirContext { return retVal; } - // /** - // * Return an unmodifiable collection containing all known resource definitions - // */ - // public Collection getResourceDefinitions() { - // - // Set> datatypes = Collections.emptySet(); - // Map, BaseRuntimeElementDefinition> existing = Collections.emptyMap(); - // HashMap> types = new HashMap>(); - // ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); - // for (int next : types.) - // - // return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); - // } - /** * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed * for extending the core library. @@ -412,10 +482,40 @@ public class FhirContext { return myIdToResourceDefinition.values(); } + /** + * Returns an unmodifiable set containing all resource names known to this + * context + */ + public Set getResourceNames() { + Set resourceNames = new HashSet<>(); + + if (myNameToResourceDefinition.isEmpty()) { + Properties props = new Properties(); + try { + props.load(myVersion.getFhirVersionPropertiesFile()); + } catch (IOException theE) { + throw new ConfigurationException("Failed to load version properties file"); + } + Enumeration propNames = props.propertyNames(); + while (propNames.hasMoreElements()) { + String next = (String) propNames.nextElement(); + if (next.startsWith("resource.")) { + resourceNames.add(next.substring("resource.".length()).trim()); + } + } + } + + for (RuntimeResourceDefinition next : myNameToResourceDefinition.values()) { + resourceNames.add(next.getName()); + } + + return Collections.unmodifiableSet(resourceNames); + } + /** * Get the restful client factory. If no factory has been set, this will be initialized with * a new ApacheRestfulClientFactory. - * + * * @return the factory used to create the restful clients */ public IRestfulClientFactory getRestfulClientFactory() { @@ -429,6 +529,16 @@ public class FhirContext { return myRestfulClientFactory; } + /** + * Set the restful client factory + * + * @param theRestfulClientFactory + */ + public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) { + Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null"); + this.myRestfulClientFactory = theRestfulClientFactory; + } + public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() { validateInitialized(); return myRuntimeChildUndeclaredExtensionDefinition; @@ -438,7 +548,7 @@ public class FhirContext { * Returns the validation support module configured for this context, creating a default * implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)} * method - * + * * @see #setValidationSupport(IContextValidationSupport) */ public IContextValidationSupport getValidationSupport() { @@ -448,6 +558,15 @@ public class FhirContext { return myValidationSupport; } + /** + * Sets the validation support module to use for this context. The validation support module + * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) + * as well as to provide terminology services to modules such as the validator and FluentPath executor + */ + public void setValidationSupport(IContextValidationSupport theValidationSupport) { + myValidationSupport = theValidationSupport; + } + public IFhirVersion getVersion() { return myVersion; } @@ -455,7 +574,7 @@ public class FhirContext { /** * Returns true if any default types for specific profiles have been defined * within this context. - * + * * @see #setDefaultTypeForProfile(String, Class) * @see #getDefaultTypeForProfile(String) */ @@ -483,7 +602,7 @@ public class FhirContext { * on a context for a previous version of fhir will result in an * {@link UnsupportedOperationException} *

- * + * * @since 2.2 */ public IFluentPath newFluentPath() { @@ -492,7 +611,7 @@ public class FhirContext { /** * Create and return a new JSON parser. - * + * *

* Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread * or every message being parsed/encoded. @@ -513,19 +632,16 @@ public class FhirContext { * sub-interface {@link IBasicClient}). See the RESTful Client documentation for more * information on how to define this interface. - * + * *

* Performance Note: This method is cheap to call, and may be called once for every operation invocation * without incurring any performance penalty *

- * - * @param theClientType - * The client type, which is an interface type to be instantiated - * @param theServerBase - * The URL of the base for the restful FHIR server to connect to + * + * @param theClientType The client type, which is an interface type to be instantiated + * @param theServerBase The URL of the base for the restful FHIR server to connect to * @return A newly created client - * @throws ConfigurationException - * If the interface type is not an interface + * @throws ConfigurationException If the interface type is not an interface */ public T newRestfulClient(Class theClientType, String theServerBase) { return getRestfulClientFactory().newClient(theClientType, theServerBase); @@ -535,14 +651,13 @@ public class FhirContext { * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against * a compliant server, but does not have methods defining the specific functionality required (as is the case with * {@link #newRestfulClient(Class, String) non-generic clients}). - * + * *

* Performance Note: This method is cheap to call, and may be called once for every operation invocation * without incurring any performance penalty *

- * - * @param theServerBase - * The URL of the base for the restful FHIR server to connect to + * + * @param theServerBase The URL of the base for the restful FHIR server to connect to */ public IGenericClient newRestfulGenericClient(String theServerBase) { return getRestfulClientFactory().newGenericClient(theServerBase); @@ -569,7 +684,7 @@ public class FhirContext { /** * Create and return a new XML parser. - * + * *

* Thread safety: Parsers are not guaranteed to be thread safe. Create a new parser instance for every thread * or every message being parsed/encoded. @@ -592,9 +707,8 @@ public class FhirContext { * THREAD SAFETY WARNING: This method is not thread safe. It should be called before any * threads are able to call any methods on this context. *

- * - * @param theType - * The custom type to add (must not be null) + * + * @param theType The custom type to add (must not be null) */ public void registerCustomType(Class theType) { Validate.notNull(theType, "theType must not be null"); @@ -612,9 +726,8 @@ public class FhirContext { * THREAD SAFETY WARNING: This method is not thread safe. It should be called before any * threads are able to call any methods on this context. *

- * - * @param theTypes - * The custom types to add (must not be null or contain null elements in the collection) + * + * @param theTypes The custom types to add (must not be null or contain null elements in the collection) */ public void registerCustomTypes(Collection> theTypes) { Validate.notNull(theTypes, "theTypes must not be null"); @@ -698,31 +811,6 @@ public class FhirContext { return classToElementDefinition; } - /** - * When encoding resources, this setting configures the parser to include - * an entry in the resource's metadata section which indicates which profile(s) the - * resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}. - *

- * This feature is intended for situations where custom resource types are being used, - * avoiding the need to manually add profile declarations for these custom types. - *

- *

- * See Profiling and Extensions - * for more information on using custom types. - *

- *

- * Note that this feature automatically adds the profile, but leaves any profile tags - * which have been manually added in place as well. - *

- * - * @param theAddProfileTagWhenEncoding - * The add profile mode (must not be null) - */ - public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) { - Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null"); - myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding; - } - /** * Sets the default type which will be used when parsing a resource that is found to be * of the given profile. @@ -732,12 +820,10 @@ public class FhirContext { * if the parser is parsing a resource and finds that it declares that it conforms to that profile, * the MyPatient type will be used unless otherwise specified. *

- * - * @param theProfile - * The profile string, e.g. "http://example.com/some_patient_profile". Must not be - * null or empty. - * @param theClass - * The resource type, or null to clear any existing type + * + * @param theProfile The profile string, e.g. "http://example.com/some_patient_profile". Must not be + * null or empty. + * @param theClass The resource type, or null to clear any existing type */ public void setDefaultTypeForProfile(String theProfile, Class theClass) { Validate.notBlank(theProfile, "theProfile must not be null or empty"); @@ -748,56 +834,19 @@ public class FhirContext { } } - /** - * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with - * caution - */ - public void setLocalizer(HapiLocalizer theMessages) { - myLocalizer = theMessages; - } - - public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) { - myNarrativeGenerator = theNarrativeGenerator; - } - /** * Sets a parser error handler to use by default on all parsers - * - * @param theParserErrorHandler - * The error handler + * + * @param theParserErrorHandler The error handler */ public void setParserErrorHandler(IParserErrorHandler theParserErrorHandler) { Validate.notNull(theParserErrorHandler, "theParserErrorHandler must not be null"); myParserErrorHandler = theParserErrorHandler; } - /** - * Sets the parser options object which will be used to supply default - * options to newly created parsers - * - * @param theParserOptions - * The parser options object - Must not be null - */ - public void setParserOptions(ParserOptions theParserOptions) { - Validate.notNull(theParserOptions, "theParserOptions must not be null"); - myParserOptions = theParserOptions; - } - /** * Sets the configured performance options - * - * @see PerformanceOptionsEnum for a list of available options - */ - public void setPerformanceOptions(Collection theOptions) { - myPerformanceOptions.clear(); - if (theOptions != null) { - myPerformanceOptions.addAll(theOptions); - } - } - - /** - * Sets the configured performance options - * + * * @see PerformanceOptionsEnum for a list of available options */ public void setPerformanceOptions(PerformanceOptionsEnum... thePerformanceOptions) { @@ -808,26 +857,7 @@ public class FhirContext { setPerformanceOptions(asList); } - /** - * Set the restful client factory - * - * @param theRestfulClientFactory - */ - public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) { - Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null"); - this.myRestfulClientFactory = theRestfulClientFactory; - } - - /** - * Sets the validation support module to use for this context. The validation support module - * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) - * as well as to provide terminology services to modules such as the validator and FluentPath executor - */ - public void setValidationSupport(IContextValidationSupport theValidationSupport) { - myValidationSupport = theValidationSupport; - } - - @SuppressWarnings({ "cast" }) + @SuppressWarnings({"cast"}) private List> toElementList(Collection> theResourceTypes) { if (theResourceTypes == null) { return null; @@ -858,13 +888,6 @@ public class FhirContext { return new FhirContext(FhirVersionEnum.DSTU2); } - /** - * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) - */ - public static FhirContext forDstu2_1() { - return new FhirContext(FhirVersionEnum.DSTU2_1); - } - /** * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference * Implementation Structures) @@ -873,9 +896,16 @@ public class FhirContext { return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG); } + /** + * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) + */ + public static FhirContext forDstu2_1() { + return new FhirContext(FhirVersionEnum.DSTU2_1); + } + /** * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3} - * + * * @since 1.4 */ public static FhirContext forDstu3() { @@ -884,14 +914,13 @@ public class FhirContext { /** * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3} - * + * * @since 3.0.0 */ public static FhirContext forR4() { return new FhirContext(FhirVersionEnum.R4); } - private static Collection> toCollection(Class theResourceType) { ArrayList> retVal = new ArrayList>(1); retVal.add(theResourceType); @@ -909,34 +938,4 @@ public class FhirContext { } return retVal; } - - /** - * Returns an unmodifiable set containing all resource names known to this - * context - */ - public Set getResourceNames() { - Set resourceNames= new HashSet<>(); - - if (myNameToResourceDefinition.isEmpty()) { - Properties props = new Properties(); - try { - props.load(myVersion.getFhirVersionPropertiesFile()); - } catch (IOException theE) { - throw new ConfigurationException("Failed to load version properties file"); - } - Enumeration propNames = props.propertyNames(); - while (propNames.hasMoreElements()){ - String next = (String) propNames.nextElement(); - if (next.startsWith("resource.")) { - resourceNames.add(next.substring("resource.".length()).trim()); - } - } - } - - for (RuntimeResourceDefinition next : myNameToResourceDefinition.values()) { - resourceNames.add(next.getName()); - } - - return Collections.unmodifiableSet(resourceNames); - } } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml index 9a96c590af6..730679bd5bb 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/resources/logback-cli-on.xml @@ -31,6 +31,13 @@ + + + + + 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 4d9d0af6c3b..3430564b65f 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 @@ -2060,10 +2060,6 @@ public abstract class BaseHapiFhirDao implements IDao, */ if (theCreateNewHistoryEntry) { final ResourceHistoryTable historyEntry = theEntity.toHistory(); -// if (theEntity.getVersion() > 1) { -// existing = myResourceHistoryTableDao.findForIdAndVersion(theEntity.getId(), theEntity.getVersion()); -// ourLog.warn("Reusing existing history entry entity {}", theEntity.getIdDt().getValue()); -// } historyEntry.setEncoding(changed.getEncoding()); historyEntry.setResource(changed.getResource()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java index 23b3910c3b1..ce72e7228c3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java @@ -21,9 +21,9 @@ import java.util.List; * 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. @@ -43,6 +43,9 @@ public interface ITermConceptDao extends JpaRepository { @Query("SELECT t FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid") Slice findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid); + @Query("SELECT COUNT(t) FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid") + Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid); + @Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null") Page findResourcesRequiringReindexing(Pageable thePageRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java index 8b90ab5a673..38c8346b790 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java @@ -32,4 +32,7 @@ public interface ITermConceptDesignationDao extends JpaRepository findByCodeSystemVersion(Pageable thePage, @Param("csv_pid") Long thePid); + @Query("SELECT COUNT(t) FROM TermConceptDesignation t WHERE t.myCodeSystemVersion.myId = :csv_pid") + Integer countByCodeSystemVersion(@Param("csv_pid") Long thePid); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java index 3b7d25d1feb..d60a96ad8f0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptParentChildLinkDao.java @@ -18,9 +18,9 @@ import java.util.Collection; * 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. @@ -31,6 +31,9 @@ import java.util.Collection; public interface ITermConceptParentChildLinkDao extends JpaRepository { + @Query("SELECT COUNT(t) FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid") + Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid); + @Query("SELECT t.myParentPid FROM TermConceptParentChildLink t WHERE t.myChildPid = :child_pid") Collection findAllWithChild(@Param("child_pid") Long theConceptPid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java index 446e426e340..37d276e6e1c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java @@ -32,4 +32,6 @@ public interface ITermConceptPropertyDao extends JpaRepository findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid); + @Query("SELECT COUNT(t) FROM TermConceptProperty t WHERE t.myCodeSystemVersion.myId = :cs_pid") + Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 23397616bc0..2caab674724 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; @@ -35,6 +36,8 @@ import org.hl7.fhir.dstu3.model.ValueSet.*; import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -223,6 +226,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 if (vs != null) { ValueSet expansion = doExpand(vs); List contains = expansion.getExpansion().getContains(); + ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); if (result != null) { if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) { @@ -238,6 +242,9 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 } + + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoValueSetDstu3.class); + private String toStringOrNull(IPrimitiveType thePrimitive) { return thePrimitive != null ? thePrimitive.getValue() : null; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 2f331777292..a7554c0b114 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -66,6 +66,7 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; @@ -82,6 +83,7 @@ import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -273,74 +275,43 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, myEntityManager.flush(); } - public void deleteCodeSystemVersion(Long theCodeSystemVersionPid) { + public void deleteCodeSystemVersion(final Long theCodeSystemVersionPid) { ourLog.info(" * Deleting code system version {}", theCodeSystemVersionPid); - PageRequest page = PageRequest.of(0, 1000); - int count; + PageRequest page1000 = PageRequest.of(0, 1000); // Parent/Child links - ourLog.info(" * Deleting parent/child links"); - count = 0; - while (true) { - Slice link = myConceptParentChildLinkDao.findByCodeSystemVersion(page, theCodeSystemVersionPid); - if (link.hasContent() == false) { - break; - } - - myConceptParentChildLinkDao.deleteInBatch(link); - - count += link.getNumberOfElements(); - ourLog.info(" * {} parent/child links deleted", count); + { + String descriptor = "parent/child links"; + Supplier> loader = () -> myConceptParentChildLinkDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid); + Supplier counter = () -> myConceptParentChildLinkDao.countByCodeSystemVersion(theCodeSystemVersionPid); + doDelete(descriptor, loader, counter, myConceptParentChildLinkDao); } - myConceptParentChildLinkDao.flush(); // Properties - ourLog.info(" * Deleting properties"); - count = 0; - while (true) { - Slice link = myConceptPropertyDao.findByCodeSystemVersion(page, theCodeSystemVersionPid); - if (link.hasContent() == false) { - break; - } - - myConceptPropertyDao.deleteInBatch(link); - - count += link.getNumberOfElements(); - ourLog.info(" * {} concept properties deleted", count); + { + String descriptor = "concept properties"; + Supplier> loader = () -> myConceptPropertyDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid); + Supplier counter = () -> myConceptPropertyDao.countByCodeSystemVersion(theCodeSystemVersionPid); + doDelete(descriptor, loader, counter, myConceptPropertyDao); } - myConceptPropertyDao.flush(); - // Properties - ourLog.info(" * Deleting designations"); - count = 0; - while (true) { - Slice link = myConceptDesignationDao.findByCodeSystemVersion(page, theCodeSystemVersionPid); - if (link.hasContent() == false) { - break; - } - - myConceptDesignationDao.deleteInBatch(link); - - count += link.getNumberOfElements(); - ourLog.info(" * {} concept designations deleted", count); + // Designations + { + String descriptor = "concept designations"; + Supplier> loader = () -> myConceptDesignationDao.findByCodeSystemVersion(page1000, theCodeSystemVersionPid); + Supplier counter = () -> myConceptDesignationDao.countByCodeSystemVersion(theCodeSystemVersionPid); + doDelete(descriptor, loader, counter, myConceptDesignationDao); } - myConceptDesignationDao.flush(); // Concepts - ourLog.info(" * Deleting concepts"); - count = 0; - while (true) { - Slice link = myConceptDao.findByCodeSystemVersion(page, theCodeSystemVersionPid); - if (link.hasContent() == false) { - break; - } - - myConceptDao.deleteInBatch(link); - myConceptDao.flush(); - - count += link.getNumberOfElements(); - ourLog.info(" * {} concepts deleted", count); + { + String descriptor = "concepts"; + // For some reason, concepts are much slower to delete, so use a smaller batch size + PageRequest page100 = PageRequest.of(0, 100); + Supplier> loader = () -> myConceptDao.findByCodeSystemVersion(page100, theCodeSystemVersionPid); + Supplier counter = () -> myConceptDao.countByCodeSystemVersion(theCodeSystemVersionPid); + doDelete(descriptor, loader, counter, myConceptDao); } Optional codeSystemOpt = myCodeSystemDao.findWithCodeSystemVersionAsCurrentVersion(theCodeSystemVersionPid); @@ -356,6 +327,26 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } + private void doDelete(String theDescriptor, Supplier> theLoader, Supplier theCounter, JpaRepository theDao) { + int count; + ourLog.info(" * Deleting {}", theDescriptor); + int totalCount = theCounter.get(); + StopWatch sw = new StopWatch(); + count = 0; + while (true) { + Slice link = theLoader.get(); + if (link.hasContent() == false) { + break; + } + + theDao.deleteInBatch(link); + + count += link.getNumberOfElements(); + ourLog.info(" * {} {} deleted - {}/sec - ETA: {}", count, theDescriptor, sw.formatThroughput(count, TimeUnit.SECONDS), sw.getEstimatedTimeRemaining(count, totalCount)); + } + theDao.flush(); + } + public void deleteConceptMap(ResourceTable theResourceTable) { // Get existing entity so it can be deleted. Optional optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(theResourceTable.getId()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 34daa680a1d..26ced1d0dfa 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.provider.dstu3; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -10,8 +11,11 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -109,6 +113,16 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); } + private void createLocalVsWithIncludeConcept() { + myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("A"); + include.addConcept().setCode("AA"); + myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); + } + private void createLocalVsWithUnknownCode(CodeSystem codeSystem) { myLocalVs = new ValueSet(); myLocalVs.setUrl(URL_MY_VALUE_SET); @@ -172,7 +186,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 * $expand?identifier=foo is legacy.. It's actually not valid in FHIR as of STU3 * but we supported it for longer than we should have so I don't want to delete * it right now. - * + *

* https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer */ @Test @@ -462,6 +476,29 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); } + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { + createLocalCsAndVs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM) + + "&code=AA"; + + ourLog.info("* Requesting: {}", url); + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + @Test public void testValidateCodeOperationByCodeAndSystemType() { //@formatter:off diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index df5af636f79..7f1171a64b2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -10,8 +10,11 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.util.UrlUtil; +import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -72,7 +75,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } private void createLocalCsAndVs() { - //@formatter:off CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.COMPLETE); @@ -86,10 +88,17 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { .addConcept().setCode("B").setDisplay("Code B") .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); - //@formatter:on myCodeSystemDao.create(codeSystem, mySrd); + } - createLocalVs(codeSystem); + private void createLocalVsWithIncludeConcept() { + myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addConcept().setCode("A"); + include.addConcept().setCode("AA"); + myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); } private void createLocalVs(CodeSystem codeSystem) { @@ -97,7 +106,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { myLocalVs.setUrl(URL_MY_VALUE_SET); ConceptSetComponent include = myLocalVs.getCompose().addInclude(); include.setSystem(codeSystem.getUrl()); - include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childAA"); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless(); } @@ -119,7 +128,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandById() throws IOException { + public void testExpandById() { //@formatter:off Parameters respParam = myClient .operation() @@ -149,7 +158,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandByIdWithFilter() throws IOException { + public void testExpandByIdWithFilter() { //@formatter:off Parameters respParam = myClient @@ -208,7 +217,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test - public void testExpandInlineVsAgainstBuiltInCs() throws IOException { + public void testExpandInlineVsAgainstBuiltInCs() { createLocalVsPointingAtBuiltInCodeSystem(); assertNotNull(myLocalValueSetId); @@ -229,7 +238,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandInlineVsAgainstExternalCs() throws IOException { + public void testExpandInlineVsAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalVs); myLocalVs.setId(""); @@ -304,7 +313,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandLocalVsAgainstBuiltInCs() throws IOException { + public void testExpandLocalVsAgainstBuiltInCs() { createLocalVsPointingAtBuiltInCodeSystem(); assertNotNull(myLocalValueSetId); @@ -325,7 +334,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandLocalVsAgainstExternalCs() throws IOException { + public void testExpandLocalVsAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalValueSetId); @@ -349,7 +358,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException { + public void testExpandLocalVsCanonicalAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalValueSetId); @@ -373,7 +382,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testExpandLocalVsWithUnknownCode() throws IOException { + public void testExpandLocalVsWithUnknownCode() { createExternalCsAndLocalVsWithUnknownCode(); assertNotNull(myLocalValueSetId); @@ -400,8 +409,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { HttpPost post = new HttpPost(ourServerBase + "/ValueSet/%24expand"); post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW))); - CloseableHttpResponse resp = ourHttpClient.execute(post); - try { + try (CloseableHttpResponse resp = ourHttpClient.execute(post)) { String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(respString); @@ -411,14 +419,11 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals(400, resp.getStatusLine().getStatusCode()); assertThat(respString, containsString("Unknown FilterOperator code 'n'")); - } finally { - IOUtils.closeQuietly(resp); } } @Test public void testValidateCodeOperationByCodeAndSystemInstance() { - //@formatter:off Parameters respParam = myClient .operation() .onInstance(myExtensionalVsId) @@ -426,7 +431,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { .withParameter(Parameters.class, "code", new CodeType("8495-4")) .andParameter("system", new UriType("http://acme.org")) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); @@ -434,9 +438,51 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); } + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnType() throws IOException { + createLocalCsAndVs(); + + String url = ourServerBase + + "/ValueSet/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { + createLocalCsAndVs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + ourLog.info("* Requesting: {}", url); + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + @Test public void testValidateCodeOperationByCodeAndSystemType() { - //@formatter:off Parameters respParam = myClient .operation() .onType(ValueSet.class) @@ -444,7 +490,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { .withParameter(Parameters.class, "code", new CodeType("8450-9")) .andParameter("system", new UriType("http://acme.org")) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java index daf108cdded..ce8c0a5712b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; +import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.util.TestUtil; import com.google.common.collect.Lists; @@ -52,17 +53,6 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { .findFirst(); } - private Optional getPropertyPart(Parameters theParameters, String thePropName, String thePart) { - return theParameters - .getParameter() - .stream() - .filter(t -> t.getName().equals(thePropName)) - .flatMap(t -> t.getPart().stream()) - .filter(t -> t.getName().equals(thePart)) - .map(t -> (T) t.getValue()) - .findFirst(); - } - @Test public void testExpandWithPropertyCoding() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); @@ -169,7 +159,6 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { } - @Test public void testLookupWithProperties2() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); @@ -189,7 +178,6 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { assertEquals("Large unstained cells/100 leukocytes", propertyValue.get().getDisplay()); } - @Test public void testLookupWithPropertiesExplicit() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); @@ -214,6 +202,30 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { } + @Test + public void testValidateCodeFound() throws Exception { + ZipCollectionBuilder files = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + myLoader.loadLoinc(files.getFiles(), mySrd); + + IFhirResourceDaoValueSet.ValidateCodeResult result = myValueSetDao.validateCode(null, null, new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, null, null, mySrd); + + assertTrue(result.isResult()); + assertEquals("Found code", result.getMessage()); + } + + @Test + public void testValidateCodeNotFound() throws Exception { + ZipCollectionBuilder files = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + myLoader.loadLoinc(files.getFiles(), mySrd); + + IFhirResourceDaoValueSet.ValidateCodeResult result = myValueSetDao.validateCode(null, null, new StringType("10013-1-9999999999"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, null, null, mySrd); + + assertFalse(result.isResult()); + assertEquals("Code not found", result.getMessage()); + } + private Set toExpandedCodes(ValueSet theExpanded) { return theExpanded .getExpansion() diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index 38283eb556e..979d758e85b 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -26,11 +26,20 @@ import ca.uhn.fhir.jpa.dao.*; @Configuration public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jpa.config${package_suffix}.Base${versionCapitalized}Config { + /** + * Subclasses may override + */ + protected boolean isSupported(String theResourceType) { + return true; + } + @Bean(name="myResourceProviders${versionCapitalized}") public List resourceProviders${versionCapitalized}() { List retVal = new ArrayList(); #foreach ( $res in $resources ) - retVal.add(rp${res.declaringClassNameComplete}${versionCapitalized}()); + if (isSupported("${res.name}")) { + retVal.add(rp${res.declaringClassNameComplete}${versionCapitalized}()); + } #end return retVal; } @@ -39,7 +48,9 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp public List> resourceDaos${versionCapitalized}() { List> retVal = new ArrayList>(); #foreach ( $res in $resources ) - retVal.add(dao${res.declaringClassNameComplete}${versionCapitalized}()); + if (isSupported("${res.name}")) { + retVal.add(dao${res.declaringClassNameComplete}${versionCapitalized}()); + } #end return retVal; }