diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000000..3829daa7291
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+NOTE: Before filing a ticket, please see the following URL:
+https://github.com/jamesagnew/hapi-fhir/wiki/Getting-Help
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Environment (please complete the following information):**
+ - HAPI FHIR Version
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/README.md b/README.md
index ad66edaa9d7..d753df33492 100644
--- a/README.md
+++ b/README.md
@@ -17,3 +17,5 @@ A demonstration of this project is available here:
http://hapi.fhir.org/
This project is Open Source, licensed under the Apache Software License 2.0.
+
+Please see [this wiki page](https://github.com/jamesagnew/hapi-fhir/wiki/Getting-Help) for information on where to get help with HAPI FHIR. Please see [Smile CDR](https://smilecdr.com) for information on commercial support.
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..86514b5ae53 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
@@ -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 extends IBaseResource> 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.
+ *
+ * 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 extends IBaseResource> 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 extends IBase> 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.
- *
- * 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 extends IBaseResource> 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 extends IBaseResource> 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-base/src/main/java/ca/uhn/fhir/util/UrlPathTokenizer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlPathTokenizer.java
index 0e60d47a30f..7c7a99f2915 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlPathTokenizer.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlPathTokenizer.java
@@ -9,9 +9,9 @@ package ca.uhn.fhir.util;
* 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.
@@ -34,8 +34,17 @@ public class UrlPathTokenizer {
return myTok.hasMoreTokens();
}
- public String nextToken() {
- return UrlUtil.unescape(myTok.nextToken());
+ /**
+ * Returns the next portion. Any URL-encoding is undone, but we will
+ * HTML encode the < and " marks since they are both
+ * not useful un URL paths in FHIR and potentially represent injection
+ * attacks.
+ *
+ * @see UrlUtil#sanitizeUrlPart(String)
+ * @see UrlUtil#unescape(String)
+ */
+ public String nextTokenUnescapedAndSanitized() {
+ return UrlUtil.sanitizeUrlPart(UrlUtil.unescape(myTok.nextToken()));
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java
index 627b6efefa6..5061148b7c1 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java
@@ -25,9 +25,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
* 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.
@@ -70,7 +70,7 @@ public class UrlUtil {
return theExtensionUrl;
}
if (theExtensionUrl == null) {
- return theExtensionUrl;
+ return null;
}
int parentLastSlashIdx = theParentExtensionUrl.lastIndexOf('/');
@@ -119,6 +119,18 @@ public class UrlUtil {
return value.startsWith("http://") || value.startsWith("https://");
}
+ public static boolean isNeedsSanitization(String theString) {
+ if (theString != null) {
+ for (int i = 0; i < theString.length(); i++) {
+ char nextChar = theString.charAt(i);
+ if (nextChar == '<' || nextChar == '"') {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public static boolean isValid(String theUrl) {
if (theUrl == null || theUrl.length() < 8) {
return false;
@@ -164,7 +176,7 @@ public class UrlUtil {
}
public static Map parseQueryString(String theQueryString) {
- HashMap> map = new HashMap>();
+ HashMap> map = new HashMap<>();
parseQueryString(theQueryString, map);
return toQueryStringMap(map);
}
@@ -197,17 +209,13 @@ public class UrlUtil {
nextKey = unescape(nextKey);
nextValue = unescape(nextValue);
- List list = map.get(nextKey);
- if (list == null) {
- list = new ArrayList<>();
- map.put(nextKey, list);
- }
+ List list = map.computeIfAbsent(nextKey, k -> new ArrayList<>());
list.add(nextValue);
}
}
public static Map parseQueryStrings(String... theQueryString) {
- HashMap> map = new HashMap>();
+ HashMap> map = new HashMap<>();
for (String next : theQueryString) {
parseQueryString(next, map);
}
@@ -222,7 +230,6 @@ public class UrlUtil {
*
[Resource Type]/[Resource ID]/_history/[Version ID]
*
*/
- //@formatter:on
public static UrlParts parseUrl(String theUrl) {
String url = theUrl;
UrlParts retVal = new UrlParts();
@@ -243,7 +250,7 @@ public class UrlUtil {
retVal.setVersionId(id.getVersionIdPart());
return retVal;
}
- if (url.matches("\\/[a-zA-Z]+\\?.*")) {
+ if (url.matches("/[a-zA-Z]+\\?.*")) {
url = url.substring(1);
}
int nextStart = 0;
@@ -282,12 +289,47 @@ public class UrlUtil {
}
- //@formatter:off
+ /**
+ * This method specifically HTML-encodes the " and
+ * < characters in order to prevent injection attacks
+ */
+ public static String sanitizeUrlPart(String theString) {
+ if (theString == null) {
+ return null;
+ }
+
+ boolean needsSanitization = isNeedsSanitization(theString);
+
+ if (needsSanitization) {
+ // Ok, we're sanitizing
+ StringBuilder buffer = new StringBuilder(theString.length() + 10);
+ for (int j = 0; j < theString.length(); j++) {
+
+ char nextChar = theString.charAt(j);
+ switch (nextChar) {
+ case '"':
+ buffer.append(""");
+ break;
+ case '<':
+ buffer.append("<");
+ break;
+ default:
+ buffer.append(nextChar);
+ break;
+ }
+
+ } // for build escaped string
+
+ return buffer.toString();
+ }
+
+ return theString;
+ }
private static Map toQueryStringMap(HashMap> map) {
- HashMap retVal = new HashMap();
+ HashMap retVal = new HashMap<>();
for (Entry> nextEntry : map.entrySet()) {
- retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[nextEntry.getValue().size()]));
+ retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[0]));
}
return retVal;
}
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-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 5dd55c8bf31..000e0eb2a48 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
@@ -1103,7 +1103,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings("unchecked")
@Override
public Object execute() {
- if (myOperationName != null && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE)) {
+ if (myOperationName != null && myOperationName.equals(Constants.EXTOP_PROCESS_MESSAGE) && myMsgBundle != null) {
Map> urlParams = new LinkedHashMap>();
// Set Url parameter Async and Response-Url
if (myIsAsync != null) {
diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java
index 8dc61aa7d0d..86724743f68 100644
--- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java
+++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java
@@ -57,17 +57,6 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
private IIdType myForceResourceId;
-
- public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource, Map> theParams, String... theUrlPath) {
- super(theContext);
- myResource = theResource;
- myUrlPath = StringUtils.join(theUrlPath, '/');
- myResources = null;
- myContents = null;
- myParams = theParams;
- myBundleType = null;
- }
-
public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource, String theUrlPath) {
super(theContext);
myResource = theResource;
@@ -105,17 +94,6 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
- public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, Map> theParams, String... theUrlPath) {
- super(theContext);
- myResource = null;
- myUrlPath = StringUtils.join(theUrlPath, '/');
- myResources = null;
- myContents = theContents;
- myParams = theParams;
- myBundleType = null;
- }
-
-
@Override
public IHttpRequest asHttpRequest(String theUrlBase, Map> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) throws DataFormatException {
StringBuilder url = new StringBuilder();
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..4b643375b78 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
@@ -1167,7 +1167,7 @@ public abstract class BaseHapiFhirDao implements IDao,
public SearchBuilder newSearchBuilder() {
SearchBuilder builder = new SearchBuilder(
getContext(), myEntityManager, myFulltextSearchSvc, this, myResourceIndexedSearchParamUriDao,
- myForcedIdDao, myTerminologySvc, mySerarchParamRegistry, myResourceTagDao, myResourceViewDao);
+ myForcedIdDao, myTerminologySvc, mySerarchParamRegistry, myResourceTagDao, myResourceViewDao);
return builder;
}
@@ -1301,20 +1301,24 @@ public abstract class BaseHapiFhirDao implements IDao,
}
}
- // Don't keep duplicate tags
+ Set allTagsNew = getAllTagDefinitions(theEntity);
Set allDefsPresent = new HashSet<>();
- theEntity.getTags().removeIf(theResourceTag -> !allDefsPresent.add(theResourceTag.getTag()));
+ allTagsNew.forEach(tag -> {
- // Remove any tags that have been removed
- for (ResourceTag next : allTagsOld) {
- if (!allDefs.contains(next)) {
- if (shouldDroppedTagBeRemovedOnUpdate(theRequest, next)) {
- theEntity.getTags().remove(next);
+ // Don't keep duplicate tags
+ if (!allDefsPresent.add(tag.getTag())) {
+ theEntity.getTags().remove(tag);
+ }
+
+ // Drop any tags that have been removed
+ if (!allDefs.contains(tag)) {
+ if (shouldDroppedTagBeRemovedOnUpdate(theRequest, tag)) {
+ theEntity.getTags().remove(tag);
}
}
- }
- Set allTagsNew = getAllTagDefinitions(theEntity);
+ });
+
if (!allTagsOld.equals(allTagsNew)) {
changed = true;
}
@@ -1473,6 +1477,15 @@ public abstract class BaseHapiFhirDao implements IDao,
return retVal;
}
+ /**
+ * Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
+ *
+ * @param theEntity The resource
+ */
+ protected void postDelete(ResourceTable theEntity) {
+ // nothing
+ }
+
/**
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
*
@@ -1483,15 +1496,6 @@ public abstract class BaseHapiFhirDao implements IDao,
// nothing
}
- /**
- * Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
- *
- * @param theEntity The resource
- */
- protected void postDelete(ResourceTable theEntity) {
- // nothing
- }
-
/**
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
*
@@ -1626,20 +1630,20 @@ public abstract class BaseHapiFhirDao implements IDao,
@SuppressWarnings("unchecked")
@Override
public R toResource(Class theResourceType, IBaseResourceEntity theEntity, Collection theTagList, boolean theForHistoryOperation) {
-
+
// 1. get resource, it's encoding and the tags if any
byte[] resourceBytes = null;
ResourceEncodingEnum resourceEncoding = null;
Collection extends BaseTag> myTagList = null;
-
+
if (theEntity instanceof ResourceHistoryTable) {
ResourceHistoryTable history = (ResourceHistoryTable) theEntity;
resourceBytes = history.getResource();
resourceEncoding = history.getEncoding();
myTagList = history.getTags();
} else if (theEntity instanceof ResourceTable) {
- ResourceTable resource = (ResourceTable)theEntity;
- ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(theEntity.getId(), theEntity.getVersion());
+ ResourceTable resource = (ResourceTable) theEntity;
+ ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(theEntity.getId(), theEntity.getVersion());
if (history == null) {
return null;
}
@@ -1648,7 +1652,7 @@ public abstract class BaseHapiFhirDao implements IDao,
myTagList = resource.getTags();
} else if (theEntity instanceof ResourceSearchView) {
// This is the search View
- ResourceSearchView myView = (ResourceSearchView)theEntity;
+ ResourceSearchView myView = (ResourceSearchView) theEntity;
resourceBytes = myView.getResource();
resourceEncoding = myView.getEncoding();
if (theTagList == null)
@@ -1663,7 +1667,7 @@ public abstract class BaseHapiFhirDao implements IDao,
// 2. get The text
String resourceText = null;
switch (resourceEncoding) {
- case JSON:
+ case JSON:
try {
resourceText = new String(resourceBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
@@ -1676,7 +1680,7 @@ public abstract class BaseHapiFhirDao implements IDao,
case DEL:
break;
}
-
+
// 3. Use the appropriate custom type if one is specified in the context
Class resourceType = theResourceType;
if (myContext.hasDefaultTypeForProfile()) {
@@ -2046,6 +2050,7 @@ public abstract class BaseHapiFhirDao implements IDao,
postPersist(theEntity, (T) theResource);
} else if (theEntity.getDeleted() != null) {
+ theEntity = myEntityManager.merge(theEntity);
postDelete(theEntity);
@@ -2060,10 +2065,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());
@@ -2195,12 +2196,11 @@ public abstract class BaseHapiFhirDao implements IDao,
} // if thePerformIndexing
- theEntity = myEntityManager.merge(theEntity);
-
if (theResource != null) {
populateResourceIdFromEntity(theEntity, theResource);
}
+
return theEntity;
}
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 411ea977d39..b8a5df18c3f 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
@@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
* 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.
@@ -395,16 +395,6 @@ public abstract class BaseHapiFhirResourceDao extends B
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
}
createForcedIdIfNeeded(entity, theResource.getIdElement());
-
- if (entity.getForcedId() != null) {
- try {
- translateForcedIdToPid(getResourceName(), theResource.getIdElement().getIdPart());
- throw new UnprocessableEntityException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "duplicateCreateForcedId", theResource.getIdElement().getIdPart()));
- } catch (ResourceNotFoundException e) {
- // good, this ID doesn't exist so we can create it
- }
- }
-
}
// Notify interceptors
@@ -1211,7 +1201,9 @@ public abstract class BaseHapiFhirResourceDao extends B
}
} else {
/*
- * Note: resourcdeId will not be null or empty here, because we check it and reject requests in BaseOutcomeReturningMethodBindingWithResourceParam
+ * Note: resourceId will not be null or empty here, because we
+ * check it and reject requests in
+ * BaseOutcomeReturningMethodBindingWithResourceParam
*/
resourceId = theResource.getIdElement();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoMessageHeaderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoMessageHeaderDstu2.java
new file mode 100644
index 00000000000..19c1c627d0e
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoMessageHeaderDstu2.java
@@ -0,0 +1,38 @@
+package ca.uhn.fhir.jpa.dao;
+
+/*
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2018 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 ca.uhn.fhir.model.dstu2.resource.MessageHeader;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+
+public class FhirResourceDaoMessageHeaderDstu2 extends FhirResourceDaoDstu2 implements IFhirResourceDaoMessageHeader {
+
+ @Override
+ public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
+ return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
+ }
+
+ public static IBaseBundle throwProcessMessageNotImplemented() {
+ throw new NotImplementedOperationException("This operation is not yet implemented on this server");
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoMessageHeader.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoMessageHeader.java
new file mode 100644
index 00000000000..1bbc494b96c
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoMessageHeader.java
@@ -0,0 +1,31 @@
+package ca.uhn.fhir.jpa.dao;
+
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
+/*
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2018 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%
+ */
+
+public interface IFhirResourceDaoMessageHeader extends IFhirResourceDao {
+
+ IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage);
+
+}
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..d0f519b2729 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.
@@ -34,15 +34,18 @@ import java.util.List;
public interface ITermConceptDao extends JpaRepository {
+ @Query("SELECT COUNT(t) FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid")
+ Integer countByCodeSystemVersion(@Param("cs_pid") Long thePid);
+
@Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system AND c.myCode = :code")
TermConcept findByCodeSystemAndCode(@Param("code_system") TermCodeSystemVersion theCodeSystem, @Param("code") String theCode);
- @Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system")
- List findByCodeSystemVersion(@Param("code_system") TermCodeSystemVersion theCodeSystem);
-
@Query("SELECT t FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid")
Slice findByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
+ @Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system")
+ List findByCodeSystemVersion(@Param("code_system") TermCodeSystemVersion theCodeSystem);
+
@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..d3cb23d9896 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
@@ -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/FhirResourceDaoMessageHeaderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoMessageHeaderDstu3.java
new file mode 100644
index 00000000000..5be5bb25661
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoMessageHeaderDstu3.java
@@ -0,0 +1,36 @@
+package ca.uhn.fhir.jpa.dao.dstu3;
+
+/*
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
+import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import org.hl7.fhir.dstu3.model.MessageHeader;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+
+public class FhirResourceDaoMessageHeaderDstu3 extends FhirResourceDaoDstu3 implements IFhirResourceDaoMessageHeader {
+
+ @Override
+ public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
+ return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
+ }
+
+}
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/dao/r4/FhirResourceDaoMessageHeaderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoMessageHeaderR4.java
new file mode 100644
index 00000000000..ba8ce1d8ded
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoMessageHeaderR4.java
@@ -0,0 +1,36 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+/*
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2018 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 ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
+import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.r4.model.MessageHeader;
+
+public class FhirResourceDaoMessageHeaderR4 extends FhirResourceDaoR4 implements IFhirResourceDaoMessageHeader {
+
+ @Override
+ public IBaseBundle messageHeaderProcessMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
+ return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4.java
index 01fde26c695..53c559b446f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4.java
@@ -187,7 +187,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
*/
@Override
public HashSet extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
- HashSet retVal = new HashSet();
+ HashSet retVal = new HashSet<>();
Collection searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
@@ -290,7 +290,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
*/
@Override
public Set extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
- HashSet retVal = new HashSet();
+ HashSet retVal = new HashSet<>();
Collection searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
@@ -354,7 +354,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
*/
@Override
public Set extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
- HashSet retVal = new HashSet();
+ HashSet retVal = new HashSet<>();
String resourceName = getContext().getResourceDefinition(theResource).getName();
@@ -397,7 +397,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
} else {
if (nextObject instanceof HumanName) {
- ArrayList allNames = new ArrayList();
+ ArrayList allNames = new ArrayList<>();
HumanName nextHumanName = (HumanName) nextObject;
if (isNotBlank(nextHumanName.getFamily())) {
allNames.add(nextHumanName.getFamilyElement());
@@ -407,7 +407,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
}
} else if (nextObject instanceof Address) {
- ArrayList allNames = new ArrayList();
+ ArrayList allNames = new ArrayList<>();
Address nextAddress = (Address) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCityElement());
@@ -573,7 +573,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
- Set> haveValues = new HashSet>();
+ Set> haveValues = new HashSet<>();
for (int i = 0; i < systems.size(); i++) {
String system = systems.get(i);
String code = codes.get(i);
@@ -608,7 +608,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
@Override
public Set extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
- HashSet retVal = new HashSet();
+ HashSet retVal = new HashSet<>();
Collection searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
@@ -690,7 +690,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
IWorkerContext worker = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
- List