Merge branch 'hapifhir:master' into bug/missing-audit-events

This commit is contained in:
Jens Kristian Villadsen 2023-10-19 13:20:29 +02:00 committed by GitHub
commit 0d3807e7fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
775 changed files with 74769 additions and 36725 deletions

View File

@ -3,35 +3,30 @@ root = true
[*]
end_of_line = lf
insert_final_newline = true
tab_width = 3
indent_size = 3
indent_style = space
tab_width = 4
indent_size = 4
charset = utf-8
[*.xml]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.json]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.vm]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.java]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
continuation_indent_size=3
ij_continuation_indent_size = 4
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false

View File

@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@ -32,7 +32,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
@ -40,7 +40,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -54,4 +54,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -1001,9 +1001,9 @@ public class FhirContext {
*/
public void registerCustomType(final Class<? extends IBase> theType) {
Validate.notNull(theType, "theType must not be null");
ensureCustomTypeList();
myCustomTypes.add(theType);
myResourceNames = null;
}
/**
@ -1025,6 +1025,7 @@ public class FhirContext {
ensureCustomTypeList();
myCustomTypes.addAll(theTypes);
myResourceNames = null;
}
private BaseRuntimeElementDefinition<?> scanDatatype(final Class<? extends IElement> theResourceType) {
@ -1177,7 +1178,14 @@ public class FhirContext {
synchronized (this) {
if (!myInitialized && !myInitializing) {
myInitializing = true;
scanResourceTypes(toElementList(myResourceTypesToScan));
try {
scanResourceTypes(toElementList(myResourceTypesToScan));
} catch (Exception e) {
ourLog.error("Failed to initialize FhirContext", e);
throw e;
} finally {
myInitializing = false;
}
}
}
}

View File

@ -553,11 +553,29 @@ public interface IValidationSupport {
private String myCodeSystemVersion;
private List<BaseConceptProperty> myProperties;
private String myDisplay;
private String mySourceDetails;
public CodeValidationResult() {
super();
}
/**
* This field may contain information about what the source of the
* validation information was.
*/
public String getSourceDetails() {
return mySourceDetails;
}
/**
* This field may contain information about what the source of the
* validation information was.
*/
public CodeValidationResult setSourceDetails(String theSourceDetails) {
mySourceDetails = theSourceDetails;
return this;
}
public String getDisplay() {
return myDisplay;
}
@ -691,8 +709,9 @@ public interface IValidationSupport {
private boolean myFound;
private String mySearchedForCode;
private String mySearchedForSystem;
private List<IValidationSupport.BaseConceptProperty> myProperties;
private List<BaseConceptProperty> myProperties;
private List<ConceptDesignation> myDesignations;
private String myErrorMessage;
/**
* Constructor
@ -708,7 +727,7 @@ public interface IValidationSupport {
return myProperties;
}
public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
public void setProperties(List<BaseConceptProperty> theProperties) {
myProperties = theProperties;
}
@ -808,7 +827,7 @@ public interface IValidationSupport {
.collect(Collectors.toSet());
}
for (IValidationSupport.BaseConceptProperty next : myProperties) {
for (BaseConceptProperty next : myProperties) {
if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) {
@ -819,11 +838,11 @@ public interface IValidationSupport {
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
if (next instanceof IValidationSupport.StringConceptProperty) {
IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
if (next instanceof StringConceptProperty) {
StringConceptProperty prop = (StringConceptProperty) next;
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
} else if (next instanceof IValidationSupport.CodingConceptProperty) {
IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
} else if (next instanceof CodingConceptProperty) {
CodingConceptProperty prop = (CodingConceptProperty) next;
ParametersUtil.addPartCoding(
theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
} else {
@ -846,6 +865,14 @@ public interface IValidationSupport {
return retVal;
}
public void setErrorMessage(String theErrorMessage) {
myErrorMessage = theErrorMessage;
}
public String getErrorMessage() {
return myErrorMessage;
}
public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) {
return new LookupCodeResult()
.setFound(false)

View File

@ -1484,6 +1484,15 @@ public enum Pointcut implements IPointcut {
* to the new contents of the resource. These changes will be reflected in
* permanent storage.
* </p>
* <p>
* <b>NO-OPS:</b> If the client has submitted an update that does not actually make any changes
* (i.e. the resource they include in the PUT body is identical to the content that
* was already stored) the server may choose to ignore the update and perform
* a "NO-OP". In this case, this pointcut is still invoked, but {@link #STORAGE_PRECOMMIT_RESOURCE_UPDATED}
* will not be. Hook methods for this pointcut may make changes to the new contents of the
* resource being updated, and in this case the NO-OP will be cancelled and
* {@link #STORAGE_PRECOMMIT_RESOURCE_UPDATED} will also be invoked.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>org.hl7.fhir.instance.model.api.IBaseResource - The previous contents of the resource being updated</li>
@ -1617,6 +1626,10 @@ public enum Pointcut implements IPointcut {
* changes as storage has already occurred. Changes will not be reflected
* in storage, but may be reflected in the HTTP response.
* </p>
* <p>
* NO-OP note: See {@link #STORAGE_PRESTORAGE_RESOURCE_UPDATED} for a note on
* no-op updates when no changes are detected.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>org.hl7.fhir.instance.model.api.IBaseResource - The previous contents of the resource</li>

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.model.api.BaseIdentifiableElement;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
@ -58,6 +59,10 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
*/
public abstract UriDt getSystemElement();
public abstract StringDt getVersionElement();
public abstract BooleanDt getUserSelectedElement();
/**
* Gets the value(s) for <b>display</b> (Representation defined by the system).
* creating it if it does
@ -72,13 +77,6 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
public abstract BaseCodingDt setDisplay(String theString);
/*
todo: handle version
public abstract StringDt getVersion();
public abstract BaseCodingDt setVersion ( String theString);
*/
/**
* {@inheritDoc}
*/
@ -181,7 +179,7 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Deprecated(since = "6.0.0")
@Override
public Boolean getMissing() {
return null;
@ -193,7 +191,7 @@ public abstract class BaseCodingDt extends BaseIdentifiableElement implements IC
* @deprecated get/setMissing is not supported in StringDt. Use {@link TokenParam} instead if you
* need this functionality
*/
@Deprecated
@Deprecated(since = "6.0.0")
@Override
public IQueryParameterType setMissing(Boolean theMissing) {
throw new UnsupportedOperationException(

View File

@ -1033,7 +1033,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
writeOptionalTagWithTextNode(theEventWriter, "system", tag.getScheme());
writeOptionalTagWithTextNode(theEventWriter, "code", tag.getTerm());
writeOptionalTagWithTextNode(theEventWriter, "display", tag.getLabel());
// wipmb should we be writing the new properties here? There must be another path.
writeOptionalTagWithTextNode(theEventWriter, "version", tag.getVersion());
write(theEventWriter, "userSelected", tag.getUserSelectedBoolean());
theEventWriter.endObject();
}
theEventWriter.endArray();

View File

@ -636,7 +636,7 @@ class ParserState<T> {
BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
if (target == null) {
// This is a bug with the structures and shouldn't happen..
// This is a bug with the structures and shouldn't happen.
throw new DataFormatException(
Msg.code(1809) + "Found unexpected element '" + theChildName + "' in parent element '"
+ myDefinition.getName() + "'. Valid names are: " + child.getValidChildNames());
@ -1584,16 +1584,20 @@ class ParserState<T> {
private class TagState extends BaseState {
private static final int LABEL = 2;
private static final int NONE = 0;
private static final int TERM = 1;
private static final int LABEL = 2;
private static final int SCHEME = 3;
private static final int TERM = 1;
private static final int VERSION = 4;
private static final int USER_SELECTED = 5;
private String myLabel;
private String myScheme;
private int mySubState = 0;
private TagList myTagList;
private String myTerm;
private String myVersion;
private Boolean myUserSelected;
public TagState(TagList theTagList) {
super(null);
@ -1614,6 +1618,12 @@ class ParserState<T> {
case SCHEME:
myScheme = (value);
break;
case VERSION:
myVersion = (value);
break;
case USER_SELECTED:
myUserSelected = Boolean.valueOf(value);
break;
case NONE:
// This handles JSON encoding, which is a bit weird
enteringNewElement(null, theName);
@ -1629,7 +1639,9 @@ class ParserState<T> {
mySubState = NONE;
} else {
if (isNotEmpty(myScheme) || isNotBlank(myTerm) || isNotBlank(myLabel)) {
myTagList.addTag(myScheme, myTerm, myLabel);
Tag tag = myTagList.addTag(myScheme, myTerm, myLabel);
tag.setUserSelectedBoolean(myUserSelected);
tag.setVersion(myVersion);
}
pop();
}
@ -1646,6 +1658,10 @@ class ParserState<T> {
mySubState = SCHEME;
} else if (Tag.ATTR_LABEL.equals(theLocalPart) || "display".equals(theLocalPart)) {
mySubState = LABEL;
} else if ("userSelected".equals(theLocalPart)) {
mySubState = USER_SELECTED;
} else if ("version".equals(theLocalPart)) {
mySubState = VERSION;
} else {
throw new DataFormatException(Msg.code(1818) + "Unexpected element: " + theLocalPart);
}

View File

@ -187,7 +187,8 @@ public class XmlParser extends BaseParser {
String namespaceURI = elem.getName().getNamespaceURI();
if ("extension".equals(elem.getName().getLocalPart())) {
String localPart = elem.getName().getLocalPart();
if ("extension".equals(localPart)) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) {
@ -199,7 +200,7 @@ public class XmlParser extends BaseParser {
url = urlAttr.getValue();
}
parserState.enteringNewElementExtension(elem, url, false, getServerBaseUrl());
} else if ("modifierExtension".equals(elem.getName().getLocalPart())) {
} else if ("modifierExtension".equals(localPart)) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) {
@ -213,8 +214,7 @@ public class XmlParser extends BaseParser {
}
parserState.enteringNewElementExtension(elem, url, true, getServerBaseUrl());
} else {
String elementName = elem.getName().getLocalPart();
parserState.enteringNewElement(namespaceURI, elementName);
parserState.enteringNewElement(namespaceURI, localPart);
}
if (!heldComments.isEmpty()) {
@ -768,6 +768,11 @@ public class XmlParser extends BaseParser {
writeOptionalTagWithValue(theEventWriter, "system", tag.getScheme());
writeOptionalTagWithValue(theEventWriter, "code", tag.getTerm());
writeOptionalTagWithValue(theEventWriter, "display", tag.getLabel());
writeOptionalTagWithValue(theEventWriter, "version", tag.getVersion());
Boolean userSelected = tag.getUserSelectedBoolean();
if (userSelected != null) {
writeOptionalTagWithValue(theEventWriter, "userSelected", userSelected.toString());
}
theEventWriter.writeEndElement();
}
}

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.parser.json.BaseJsonLikeWriter;
import ca.uhn.fhir.parser.json.JsonLikeStructure;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.StreamReadConstraints;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -388,6 +389,15 @@ public class JacksonStructure implements JsonLikeStructure {
retVal = retVal.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
retVal = retVal.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
retVal = retVal.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
retVal.getFactory().setStreamReadConstraints(createStreamReadConstraints());
return retVal;
}
private static StreamReadConstraints createStreamReadConstraints() {
return StreamReadConstraints.builder()
.maxStringLength(Integer.MAX_VALUE)
.build();
}
}

View File

@ -182,6 +182,11 @@ public class Constants {
public static final String PARAM_HAS = "_has";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_INCLUDE = "_include";
/**
* @since 7.0.0
*/
public static final String PARAM_LANGUAGE = "_language";
public static final String PARAM_INCLUDE_QUALIFIER_RECURSE = ":recurse";
public static final String PARAM_INCLUDE_RECURSE = "_include" + PARAM_INCLUDE_QUALIFIER_RECURSE;
public static final String PARAM_INCLUDE_QUALIFIER_ITERATE = ":iterate";
@ -326,6 +331,9 @@ public class Constants {
*/
public static final int UUID_LENGTH = 36;
public static final String BULK_DATA_ACCESS_IG_URL =
"http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data";
/**
* Application configuration key used to enable or disable Hibernate Envers.
*/

View File

@ -40,7 +40,7 @@ public class MethodOutcome {
private IBaseResource myResource;
private Map<String, List<String>> myResponseHeaders;
private Collection<Runnable> myResourceViewCallbacks;
private int myResponseStatusCode;
private Integer myResponseStatusCode;
/**
* Constructor
@ -258,6 +258,10 @@ public class MethodOutcome {
}
public int getResponseStatusCode() {
return myResponseStatusCode;
return isResponseStatusCodeSet() ? myResponseStatusCode : 0;
}
public boolean isResponseStatusCodeSet() {
return myResponseStatusCode != null;
}
}

View File

@ -151,13 +151,27 @@ public class InternalCodingDt extends BaseCodingDt implements ICompositeDatatype
* is consistent across versions. However this cannot consistently be assured. and When the meaning is not guaranteed to be consistent, the version SHOULD be exchanged
* </p>
*/
public StringDt getVersion() {
@Override
public StringDt getVersionElement() {
if (myVersion == null) {
myVersion = new StringDt();
}
return myVersion;
}
@Override
public BooleanDt getUserSelectedElement() {
return new BooleanDt();
}
/**
* Legacy name for {@link #getVersionElement()}
*/
@Deprecated(since = "7.0.0")
public StringDt getVersion() {
return getVersionElement();
}
/**
* Sets the value(s) for <b>version</b> (Version of the system - if relevant)
*

View File

@ -48,7 +48,16 @@ public enum UriParamQualifierEnum {
* Value <code>:below</code>
* </p>
*/
BELOW(":below");
BELOW(":below"),
/**
* The contains modifier allows clients to indicate that a supplied URI input should be matched
* as a case-insensitive and combining-character insensitive match anywhere in the target URI.
* <p>
* Value <code>:contains</code>
* </p>
*/
CONTAINS(":contains");
private static final Map<String, UriParamQualifierEnum> KEY_TO_VALUE;

View File

@ -36,6 +36,8 @@ public final class HapiSystemProperties {
static final String TEST_MODE = "test";
static final String UNIT_TEST_MODE = "unit_test_mode";
static final long DEFAULT_TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS = 10 * DateUtils.MILLIS_PER_SECOND;
static final String PREVENT_INVALIDATING_CONDITIONAL_MATCH_CRITERIA =
"hapi.storage.prevent_invalidating_conditional_match_criteria";
private HapiSystemProperties() {}
@ -158,4 +160,9 @@ public final class HapiSystemProperties {
public static boolean isSuppressHapiFhirVersionLogEnabled() {
return Boolean.parseBoolean(System.getProperty(SUPPRESS_HAPI_FHIR_VERSION_LOG));
}
public static boolean isPreventInvalidatingConditionalMatchCriteria() {
return Boolean.parseBoolean(System.getProperty(
HapiSystemProperties.PREVENT_INVALIDATING_CONDITIONAL_MATCH_CRITERIA, Boolean.FALSE.toString()));
}
}

View File

@ -287,9 +287,33 @@ public class FhirTerser {
return retVal;
}
/**
* Extracts all outbound references from a resource
*
* @param theResource the resource to be analyzed
* @return a list of references to other resources
*/
public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) {
return getAllResourceReferencesExcluding(theResource, Lists.newArrayList());
}
/**
* Extracts all outbound references from a resource, excluding any that are located on black-listed parts of the
* resource
*
* @param theResource the resource to be analyzed
* @param thePathsToExclude a list of dot-delimited paths not to include in the result
* @return a list of references to other resources
*/
public List<ResourceReferenceInfo> getAllResourceReferencesExcluding(
final IBaseResource theResource, List<String> thePathsToExclude) {
final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
List<List<String>> tokenizedPathsToExclude = thePathsToExclude.stream()
.map(path -> StringUtils.split(path, "."))
.map(Lists::newArrayList)
.collect(Collectors.toList());
visit(newMap(), theResource, theResource, null, null, def, new IModelVisitor() {
@Override
public void acceptElement(
@ -301,6 +325,10 @@ public class FhirTerser {
if (theElement == null || theElement.isEmpty()) {
return;
}
if (thePathToElement != null && pathShouldBeExcluded(tokenizedPathsToExclude, thePathToElement)) {
return;
}
if (IBaseReference.class.isAssignableFrom(theElement.getClass())) {
retVal.add(new ResourceReferenceInfo(
myContext, theOuterResource, thePathToElement, (IBaseReference) theElement));
@ -310,6 +338,19 @@ public class FhirTerser {
return retVal;
}
private boolean pathShouldBeExcluded(List<List<String>> theTokenizedPathsToExclude, List<String> thePathToElement) {
return theTokenizedPathsToExclude.stream().anyMatch(p -> {
// Check whether the path to the element starts with the path to be excluded
if (p.size() > thePathToElement.size()) {
return false;
}
List<String> prefix = thePathToElement.subList(0, p.size());
return Objects.equals(p, prefix);
});
}
private BaseRuntimeChildDefinition getDefinition(
BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) {
BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0));

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.Constants;
@ -250,34 +251,77 @@ public class OperationOutcomeUtil {
public static IBase addIssueWithMessageId(
FhirContext myCtx,
IBaseOperationOutcome theOperationOutcome,
String severity,
String message,
String messageId,
String location,
String theSeverity,
String theMessage,
String theMessageId,
String theLocation,
String theCode) {
IBase issue = addIssue(myCtx, theOperationOutcome, severity, message, location, theCode);
BaseRuntimeElementCompositeDefinition<?> issueElement =
(BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition(issue.getClass());
BaseRuntimeChildDefinition detailsChildDef = issueElement.getChildByName("details");
IBase issue = addIssue(myCtx, theOperationOutcome, theSeverity, theMessage, theLocation, theCode);
if (isNotBlank(theMessageId)) {
addDetailsToIssue(myCtx, issue, Constants.JAVA_VALIDATOR_DETAILS_SYSTEM, theMessageId);
}
IPrimitiveType<?> system =
(IPrimitiveType<?>) myCtx.getElementDefinition("uri").newInstance();
system.setValueAsString(Constants.JAVA_VALIDATOR_DETAILS_SYSTEM);
IPrimitiveType<?> code =
(IPrimitiveType<?>) myCtx.getElementDefinition("code").newInstance();
code.setValueAsString(messageId);
BaseRuntimeElementCompositeDefinition<?> codingDef =
(BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition("Coding");
ICompositeType coding = (ICompositeType) codingDef.newInstance();
codingDef.getChildByName("system").getMutator().addValue(coding, system);
codingDef.getChildByName("code").getMutator().addValue(coding, code);
BaseRuntimeElementCompositeDefinition<?> ccDef =
(BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition("CodeableConcept");
ICompositeType codeableConcept = (ICompositeType) ccDef.newInstance();
ccDef.getChildByName("coding").getMutator().addValue(codeableConcept, coding);
detailsChildDef.getMutator().addValue(issue, codeableConcept);
return issue;
}
public static void addDetailsToIssue(FhirContext theFhirContext, IBase theIssue, String theSystem, String theCode) {
BaseRuntimeElementCompositeDefinition<?> issueElement =
(BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition(theIssue.getClass());
BaseRuntimeChildDefinition detailsChildDef = issueElement.getChildByName("details");
BaseRuntimeElementCompositeDefinition<?> codingDef =
(BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition("Coding");
ICompositeType coding = (ICompositeType) codingDef.newInstance();
// System
IPrimitiveType<?> system =
(IPrimitiveType<?>) theFhirContext.getElementDefinition("uri").newInstance();
system.setValueAsString(theSystem);
codingDef.getChildByName("system").getMutator().addValue(coding, system);
// Code
IPrimitiveType<?> code =
(IPrimitiveType<?>) theFhirContext.getElementDefinition("code").newInstance();
code.setValueAsString(theCode);
codingDef.getChildByName("code").getMutator().addValue(coding, code);
BaseRuntimeElementCompositeDefinition<?> ccDef =
(BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition("CodeableConcept");
ICompositeType codeableConcept = (ICompositeType) ccDef.newInstance();
ccDef.getChildByName("coding").getMutator().addValue(codeableConcept, coding);
detailsChildDef.getMutator().addValue(theIssue, codeableConcept);
}
public static void addIssueLineExtensionToIssue(FhirContext theCtx, IBase theIssue, String theLine) {
if (theCtx.getVersion().getVersion() != FhirVersionEnum.DSTU2) {
ExtensionUtil.setExtension(
theCtx,
theIssue,
"http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line",
"integer",
theLine);
}
}
public static void addIssueColExtensionToIssue(FhirContext theCtx, IBase theIssue, String theColumn) {
if (theCtx.getVersion().getVersion() != FhirVersionEnum.DSTU2) {
ExtensionUtil.setExtension(
theCtx,
theIssue,
"http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col",
"integer",
theColumn);
}
}
public static void addMessageIdExtensionToIssue(FhirContext theCtx, IBase theIssue, String theMessageId) {
if (theCtx.getVersion().getVersion() != FhirVersionEnum.DSTU2) {
ExtensionUtil.setExtension(
theCtx,
theIssue,
"http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id",
"string",
theMessageId);
}
}
}

View File

@ -403,7 +403,7 @@ public class ParametersUtil {
public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) {
IPrimitiveType<BigDecimal> value = (IPrimitiveType<BigDecimal>)
theContext.getElementDefinition("decimal").newInstance();
value.setValue(theValue == null ? null : new BigDecimal(theValue));
value.setValue(theValue == null ? null : BigDecimal.valueOf(theValue));
addPart(theContext, theParameter, theName, value);
}

View File

@ -40,6 +40,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -602,6 +604,39 @@ public class UrlUtil {
return parameters;
}
/**
* Creates list of sub URIs candidates for search with :above modifier
* Example input: http://[host]/[pathPart1]/[pathPart2]
* Example output: http://[host], http://[host]/[pathPart1], http://[host]/[pathPart1]/[pathPart2]
*
* @param theUri String URI parameter
* @return List of URI candidates
*/
public static List<String> getAboveUriCandidates(String theUri) {
try {
URI uri = new URI(theUri);
if (uri.getScheme() == null || uri.getHost() == null) {
throwInvalidRequestExceptionForNotValidUri(theUri, null);
}
} catch (URISyntaxException theCause) {
throwInvalidRequestExceptionForNotValidUri(theUri, theCause);
}
List<String> candidates = new ArrayList<>();
Path path = Paths.get(theUri);
candidates.add(path.toString().replace(":/", "://"));
while (path.getParent() != null && path.getParent().toString().contains("/")) {
candidates.add(path.getParent().toString().replace(":/", "://"));
path = path.getParent();
}
return candidates;
}
private static void throwInvalidRequestExceptionForNotValidUri(String theUri, Exception theCause) {
throw new InvalidRequestException(
Msg.code(2419) + String.format("Provided URI is not valid: %s", theUri), theCause);
}
public static class UrlParts {
private String myParams;
private String myResourceId;

View File

@ -117,11 +117,18 @@ public enum VersionEnum {
V6_4_2,
V6_5_0,
V6_6_0,
V6_6_1,
V6_6_2,
V6_7_0,
V6_8_0,
V6_8_1,
V6_8_2,
V6_9_0,
V6_10_0,
V6_11_0,
V7_0_0;
public static VersionEnum latestVersion() {

View File

@ -24,6 +24,8 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
public class SingleValidationMessage {
private Integer myLocationCol;
@ -32,6 +34,7 @@ public class SingleValidationMessage {
private String myMessage;
private String myMessageId;
private ResultSeverityEnum mySeverity;
private List<String> mySliceMessages;
/**
* Constructor
@ -58,6 +61,7 @@ public class SingleValidationMessage {
b.append(myLocationString, other.myLocationString);
b.append(myMessage, other.myMessage);
b.append(mySeverity, other.mySeverity);
b.append(mySliceMessages, other.mySliceMessages);
return b.isEquals();
}
@ -93,6 +97,7 @@ public class SingleValidationMessage {
b.append(myLocationString);
b.append(myMessage);
b.append(mySeverity);
b.append(mySliceMessages);
return b.toHashCode();
}
@ -137,6 +142,17 @@ public class SingleValidationMessage {
if (mySeverity != null) {
b.append("severity", mySeverity.getCode());
}
if (mySliceMessages != null) {
b.append("sliceMessages", mySliceMessages);
}
return b.toString();
}
public void setSliceMessages(List<String> theSliceMessages) {
mySliceMessages = theSliceMessages;
}
public List<String> getSliceMessages() {
return mySliceMessages;
}
}

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import java.util.Collections;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
@ -38,11 +39,11 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
public class ValidationResult {
public static final int ERROR_DISPLAY_LIMIT_DEFAULT = 1;
public static final String UNKNOWN = "(unknown)";
private static final String ourNewLine = System.getProperty("line.separator");
private final FhirContext myCtx;
private final boolean myIsSuccessful;
private final List<SingleValidationMessage> myMessages;
private int myErrorDisplayLimit = ERROR_DISPLAY_LIMIT_DEFAULT;
public ValidationResult(FhirContext theCtx, List<SingleValidationMessage> theMessages) {
@ -108,8 +109,8 @@ public class ValidationResult {
/**
* @deprecated Use {@link #toOperationOutcome()} instead since this method returns a view.
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
* will be removed at some point.
* {@link #toOperationOutcome()} is identical to this method, but has a more suitable name so this method
* will be removed at some point.
*/
@Deprecated
public IBaseOperationOutcome getOperationOutcome() {
@ -131,39 +132,36 @@ public class ValidationResult {
*/
public void populateOperationOutcome(IBaseOperationOutcome theOperationOutcome) {
for (SingleValidationMessage next : myMessages) {
String location;
if (isNotBlank(next.getLocationString())) {
location = next.getLocationString();
} else if (next.getLocationLine() != null || next.getLocationCol() != null) {
location = "Line[" + next.getLocationLine() + "] Col[" + next.getLocationCol() + "]";
} else {
location = null;
}
String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null;
IBase issue = OperationOutcomeUtil.addIssueWithMessageId(
myCtx,
theOperationOutcome,
severity,
next.getMessage(),
next.getMessageId(),
location,
Constants.OO_INFOSTATUS_PROCESSING);
Integer locationLine = next.getLocationLine();
Integer locationCol = next.getLocationCol();
String location = next.getLocationString();
ResultSeverityEnum issueSeverity = next.getSeverity();
String message = next.getMessage();
String messageId = next.getMessageId();
if (next.getLocationLine() != null || next.getLocationCol() != null) {
String unknown = "(unknown)";
String line = unknown;
if (next.getLocationLine() != null && next.getLocationLine() != -1) {
line = next.getLocationLine().toString();
}
String col = unknown;
if (next.getLocationCol() != null && next.getLocationCol() != -1) {
col = next.getLocationCol().toString();
}
if (!unknown.equals(line) || !unknown.equals(col)) {
OperationOutcomeUtil.addLocationToIssue(myCtx, issue, "Line " + line + ", Col " + col);
}
if (next.getSliceMessages() == null) {
addIssueToOperationOutcome(
theOperationOutcome, location, locationLine, locationCol, issueSeverity, message, messageId);
continue;
}
}
/*
* Occasionally the validator will return these lists of "slice messages"
* which happen when validating rules associated with a specific slice in
* a profile.
*/
for (String nextSliceMessage : next.getSliceMessages()) {
String combinedMessage = message + " - " + nextSliceMessage;
addIssueToOperationOutcome(
theOperationOutcome,
location,
locationLine,
locationCol,
issueSeverity,
combinedMessage,
messageId);
}
} // for
if (myMessages.isEmpty()) {
String message = myCtx.getLocalizer().getMessage(ValidationResult.class, "noIssuesDetected");
@ -171,6 +169,44 @@ public class ValidationResult {
}
}
private void addIssueToOperationOutcome(
IBaseOperationOutcome theOperationOutcome,
String location,
Integer locationLine,
Integer locationCol,
ResultSeverityEnum issueSeverity,
String message,
String messageId) {
if (isBlank(location) && locationLine != null && locationCol != null) {
location = "Line[" + locationLine + "] Col[" + locationCol + "]";
}
String severity = issueSeverity != null ? issueSeverity.getCode() : null;
IBase issue = OperationOutcomeUtil.addIssueWithMessageId(
myCtx, theOperationOutcome, severity, message, messageId, location, Constants.OO_INFOSTATUS_PROCESSING);
if (locationLine != null || locationCol != null) {
String unknown = UNKNOWN;
String line = unknown;
if (locationLine != null && locationLine != -1) {
line = locationLine.toString();
}
String col = unknown;
if (locationCol != null && locationCol != -1) {
col = locationCol.toString();
}
if (!unknown.equals(line) || !unknown.equals(col)) {
OperationOutcomeUtil.addIssueLineExtensionToIssue(myCtx, issue, line);
OperationOutcomeUtil.addIssueColExtensionToIssue(myCtx, issue, col);
String locationString = "Line[" + line + "] Col[" + col + "]";
OperationOutcomeUtil.addLocationToIssue(myCtx, issue, locationString);
}
}
if (isNotBlank(messageId)) {
OperationOutcomeUtil.addMessageIdExtensionToIssue(myCtx, issue, messageId);
}
}
@Override
public String toString() {
return "ValidationResult{" + "messageCount=" + myMessages.size() + ", isSuccessful=" + myIsSuccessful
@ -191,6 +227,4 @@ public class ValidationResult {
public void setErrorDisplayLimit(int theErrorDisplayLimit) {
myErrorDisplayLimit = theErrorDisplayLimit;
}
private static final String ourNewLine = System.getProperty("line.separator");
}

View File

@ -1,4 +1,7 @@
org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport.displayMismatch=Concept Display "{0}" does not match expected "{1}"
ca.uhn.fhir.jpa.term.TermReadSvcImpl.expansionRefersToUnknownCs=Unknown CodeSystem URI "{0}" referenced from ValueSet
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded=ValueSet "{0}" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: {1} | {2}
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotYetExpanded_OffsetNotAllowed=ValueSet expansion can not combine "offset" with "ValueSet.compose.exclude" unless the ValueSet has been pre-expanded. ValueSet "{0}" must be pre-expanded for this operation to work.
@ -6,8 +9,9 @@ ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetExpandedUsingPreExpansion=ValueSet
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetExpandedUsingInMemoryExpansion=ValueSet with URL "{0}" was expanded using an in-memory expansion
ca.uhn.fhir.jpa.term.TermReadSvcImpl.validationPerformedAgainstPreExpansion=Code validation occurred using a ValueSet expansion that was pre-calculated at {0}
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetNotFoundInTerminologyDatabase=ValueSet can not be found in terminology database: {0}
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetPreExpansionInvalidated=ValueSet with URL "{0}" precaluclated expansion with {1} concept(s) has been invalidated
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetCantInvalidateNotYetPrecalculated=ValueSet with URL "{0}" already has status: {1}
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetPreExpansionInvalidated=ValueSet with URL "{0}" precaluclated expansion with {1} concept(s) has been invalidated
ca.uhn.fhir.jpa.term.TermReadSvcImpl.valueSetCantInvalidateNotYetPrecalculated=ValueSet with URL "{0}" already has status: {1}
ca.uhn.fhir.jpa.term.TermReadSvcImpl.unknownCodeInSystem=Unknown code "{0}#{1}"
# Core Library Messages

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
@ -12,7 +12,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -270,14 +270,6 @@ public abstract class BaseApp {
// Actually execute the command
command.run(parsedOptions);
myShutdownHookHasNotRun = true;
runCleanupHookAndUnregister();
if (!HapiSystemProperties.isTestModeEnabled()) {
System.exit(0);
}
} catch (ParseException e) {
if (!HapiSystemProperties.isTestModeEnabled()) {
LogbackUtil.loggingConfigOff();
@ -296,6 +288,13 @@ public abstract class BaseApp {
ourLog.error("Error during execution: ", t);
runCleanupHookAndUnregister();
exitDueToException(new CommandFailureException("Error: " + t, t));
} finally {
myShutdownHookHasNotRun = true;
runCleanupHookAndUnregister();
if (!HapiSystemProperties.isTestModeEnabled()) {
System.exit(0);
}
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -75,8 +75,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
/**
* Constructor
*
* @param theFhirContext
* The context
* @param theFhirContext The context
*/
public RestfulClientFactory(FhirContext theFhirContext) {
myContext = theFhirContext;
@ -142,13 +141,10 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
/**
* Instantiates a new client instance
*
* @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
*/
@Override
public synchronized <T extends IRestfulClient> T newClient(Class<T> theClientType, String theServerBase) {
@ -281,13 +277,8 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
break;
case ONCE:
if (myValidatedServerBaseUrls.contains(serverBase)) {
break;
}
synchronized (myValidatedServerBaseUrls) {
if (!myValidatedServerBaseUrls.contains(serverBase)) {
myValidatedServerBaseUrls.add(serverBase);
if (myValidatedServerBaseUrls.add(serverBase)) {
validateServerBase(serverBase, theHttpClient, theClient);
}
}
@ -396,20 +387,13 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
}
String serverBase = normalizeBaseUrlForMap(theServerBase);
if (myValidatedServerBaseUrls.contains(serverBase)) {
return;
}
synchronized (myValidatedServerBaseUrls) {
myValidatedServerBaseUrls.add(serverBase);
}
myValidatedServerBaseUrls.add(serverBase);
}
/**
* Get the http client for the given server base
*
* @param theServerBase
* the server base
* @param theServerBase the server base
* @return the http client
*/
protected abstract IHttpClient getHttpClient(String theServerBase);

View File

@ -544,6 +544,7 @@ public class MethodUtil {
}
MethodOutcome retVal = new MethodOutcome();
retVal.setResponseStatusCode(theResponseStatusCode);
if (locationHeaders.size() > 0) {
String locationHeader = locationHeaders.get(0);
BaseOutcomeReturningMethodBinding.parseContentLocation(theContext, retVal, locationHeader);

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.2-SNAPSHOT</version>
<version>6.9.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.method.ResponsePage;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.Patient;
@ -63,7 +64,10 @@ public class PagingPatientProvider implements IResourceProvider {
@Nonnull
@Override
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
public List<IBaseResource> getResources(
int theFromIndex,
int theToIndex,
@Nonnull ResponsePage.ResponsePageBuilder theResponsePageBuilder) {
int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
return loadResourcesByIds(idsToReturn);

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 3786
title: "Previously, when executing an update on a resource that had to undergo MDM, a nullpointer could occur. This has been fixed."

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5089
title: "Updating storage-cr module to latest CQL 3.0.0, latest cql engine improvements, and Clinical Reasoning operations
to leverage repository api pattern. This will remove several dependencies from within hapi-fhir to make future maintenance
simpler and performance more robust."

View File

@ -0,0 +1,4 @@
---
type: add
issue: 5129
title: "Added a field that shows the total number of `POSSIBLE_DUPLICATE` links has been added to the `$mdm-duplicate-golden-resources` operation response."

View File

@ -0,0 +1,9 @@
---
type: fix
issue: 5192
title: "Fixed a bug where search Bundles with `include` entries from an _include query parameter might
trigger a 'next' link to blank pages.
Specifically, if _include'd resources + requested resources were greater than (or equal to)
requested page size, a 'next' link would be generated, even though no additional
resources are available.
"

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5196
title: "Previously, type-level expunge was allowed even if expunge operation was turned off. This is now fixed."

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 5198
title: "Resolved an issue with type-everything search operations (eg, /Patient/$everything),
where not all page results were being returned if _count was specified to be
the same value as the maximum page size to fetch.
"

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5212
title: "Previously, when a CDS hook was registered and called with a empty context, the server returned a 500. This behaviour has been fixed."

View File

@ -0,0 +1,5 @@
---
type: perf
issue: 5215
title: "When executing an HFQL search with a FHIRPath filter on the `id` element, this will now
be automatically converted into an `_id` search parameter match for better performance."

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5219
title: "Reindex batch job threw an exception when no results matched the reindex request. This has been corrected."

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 5220
title: "Fixed a race condition in RestfulClientFactory that could cause validateInitialized() to deadlock.
Fixed a race condition in FhirContext initialization that could produce a 'this.myNameToResourceType is null' NPE.
Fixed a shutdown hook memory leak in BaseApp that happened when the command threw an exception;
this memory leak only affects code that calls App.main repeatedly which is probably only in test code."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5230
title: "batch2 jobs on MS SQL Server were failing to transition to FAILED state after max retrials
for the job are exhausted. This is now fixed."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5231
title: "Previously, the `$mdm-link-history` operation would result in a 404 response when it was executed after a
`$mdm-clear`. This has now been fixed by removing link history on `$mdm-clear`."

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5235
title: "Changes have been made to allow searching on multiple patient _ids
when in a patient_id partitioned environment.
"

View File

@ -0,0 +1,13 @@
---
type: add
issue: 5236
title: "Previously, when updating an MDM link to NO_MATCH,
the golden resource involved would maintain its previous
values, as defined by survivorship service.
This would result in out-of-date golden resources with
data that might not be accurate anymore.
Now, when a link is changed to NO_MATCH, golden resources
will be rebuilt from the ground up using the MDM survivorship
service, and the set of links/source resources available at the
time of update.
"

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5238
title: "Added an implementation of Clinical Reasoning CDS on FHIR to the CDS Hooks module that allows PlanDefinition
worfklows to be processed as CDS Services using the $apply operation.
"

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5242
title: "Previously, `$mdm-clear` failed to expunge `REDIRECTED` golden resources which left them as orphans. This is now fixed."

View File

@ -0,0 +1,4 @@
---
type: add
issue: 5246
title: "Combined the ExpandResources step and WriteBinary step in the new WriteBinary step v2 for bulk exports."

View File

@ -0,0 +1,7 @@
---
type: fix
issue: 5254
title: "Previously, when bulk export is enabled the resource list would be generated and would
not be able to add a custom resource to that list. Now once a custom resource is added the list is
rebuilt.
"

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5256
title: "Added RequestDetails as part of the parameters when cleaning up possible matches to enable interceptors to
access it."

View File

@ -0,0 +1,7 @@
---
type: change
issue: 5229
title: "Previously, when using INLINE tag storage mode, a superfluous version of a resource would be created as a result
of an update request which didn't have a real logical change to the resource but only changed the order of existing
items in tag, security label or profile collections. This change prevents this behaviour. Also on resource retrieval,
these meta collections are sorted alphabetically, based on (security, code) pair for tags and security labels."

View File

@ -0,0 +1,5 @@
---
type: change
issue: 5262
title: "Previously, when both resourceId and goldenResourceId are provided to the mdm link history operation, they will
be treated as an OR. This is now changed to AND in order to comply with REST conventions."

View File

@ -0,0 +1,4 @@
---
type: fix
jira: SMILE-7307
title: "Previously, executing a Group Bulk Export without defining the `_type` parameter would accidentally omit `Patient` and `Organization` types. This has been corrected."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5268
title: "Previously, the response status code set in a `MethodOutcome` of a Resource provider was not respected.
This has been fixed."

View File

@ -0,0 +1,8 @@
---
type: add
issue: 5271
title: "The error messages returned in an OperationOutcome when validating terminology codes
as a part of resource profile validation have been improved. Machine processable location
(line/col) information is now available through a pair of dedicated extensions, and
error messages such as UCUM parsing issues are now returned to the client (previously
they were swallowed and a generic error message was returned)."

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5274
title: "Added a service for generating metrics on mdm links and resources.
This includes JPA queries and updated indices.
"

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5275
title: "Added an API that allows to configure permission rules for operations with access to all resources.
This permission is needed to allow a search across the entire patient's record in the scope of the $everything operation to access all
resources that references input Patient, including resources outside of the patient's compartment."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5275
title: "Previously, when calling `$everything` operation on a Patient instance, it was possible to retrieve data related
to another patient via a List or Group resources. This has been fixed."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5276
title: "Previously, GraphQL queries will error when using base resource search parameters such as '_id' after search parameter rebuild.
This has been fixed."

View File

@ -0,0 +1,4 @@
---
type: add
issue: 5290
title: "Added storage property to prevent conditional updates from invalidating match criteria."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5295
title: "A regression in the HAPI FHIR 6.6.0 JPA server meant that absolute resource
references which also contained an identifier were rejected even if the server
was configured to allow absolute references. This has been corrected."

View File

@ -0,0 +1,13 @@
---
type: fix
issue: 5297
title: "Several fixes have been made to the IPS generator:
<ul>
<li>The display names associated with several sections have been corrected to exactly match the LOINC definitions for their codes</li>
<li>Immunizations will now be ordered from most recent to least recent</li>
<li>IPS documents containing Consent resources for Advanced Directives could result in a crash</li>
<li>IPS documents containing Procedure resources for History of Procedures with a performed date could result in a crash</li>
<li>IPS documents containing AllergyIntolerance resources containing an occurrence but not a reaction date could result in a crash</li>
<li>IPS documents containing AllergyIntolerance resources containing an onset value in string format could result in a crash</li>
<li>IPS documents containing MedicationRequest resources with no associated Medication could result in a crash</li>
</ul>"

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5298
title: "Under some circumstances, a large `$everything` operation could enter an infinite loop and eventually timeout with a failure. This has been corrected."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5300
title: "A bug in DefaultProfileValidationSupport in R5 mode caused it to return duplicates
in the lists returned by `fetchAllStructureDefinitions()`, `fetchAllSearchParameters()`, etc.
This has been corrected."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 5300
title: "A new configuration option has been added to `StorageSettings` which enables
support in the JPA server for the `_language` SearchParameter."

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5306
title: "A new option has been added to the JPA server JpaStorageOptions which prevents the
server from maintaining a version history. In this mode, when a new version of a resource
is added, the previous version is automatically expunged."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5307
title: "Some search parameters include parenthetical expressions (e.g. `(MedicationRequest.medication as Reference)`).
The leading `(` was causing searches using parenthetical expressions to fail where the reference target was a contained resource.
This has been corrected."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5310
title: "Update DSTU2 tags and security labels with support for `userSelected` and `version` elements.
Also fix them on security labels in JPA storage."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5312
jira: SMILE-7323
title: "Previously, issuing a reindex operation for resources on a specific partition would fail. This problem has been fixed."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5316
jira: SMILE-7168
title: "Previously, performing a FHIR transaction containing both a conditional delete and a conditional update
on the same resource would fail. This has been fixed."

View File

@ -0,0 +1,8 @@
---
type: add
issue: 5321
title: "It is now possible to configure the strictness of concept display name validation
using a new flag on the InMemoryTerminologyServerValidationSupport (for non-JPA validation)
and JpaStorageSettings (for JPA validation). In addition, the error messages emitted by
the validator when a concept display doesn't match have been improved to be much
more useful."

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5322
title: "Update DSTU3 validation resources to FHIR 3.0.2 instead of 3.0.1"

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5330
jira: SMILE-7372
title: "Previously, the capability statement returned by the server would not declare conformance to IG when a bulk data
export provider is registered with the server. This issue has been fixed."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5331
title: "Previously, the score field returned by $mdm-query-links operation would contain imprecise decimal values. This
is now fixed and rounded to 4 decimal places."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5333
jira: SMILE-7403
title: "A regression was introduced in 2023.08.R01 which caused binary storage prefixes to not be applied to exported binary blobs. This has been fixed."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5336
jira: SMILE-7406
title: "Previously, on PostgreSQL, the $mdm-link-history operation would fail if all ids provided to a parameter are
unknown, and the error will persist for all subsequent requests. This is now fixed."

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5339
jira: SMILE-7236
title: "Previously, Bulk Export will error when processing a resource if the patient compartment SearchParameter of that resource is not present.
This has been fixed, the new behaviour is to ignore such resources."

View File

@ -0,0 +1,4 @@
---
type: add
issue: 5341
title: "Added registries for CdsCrServices and CrDiscoveryServices in CDS Hooks to allow registration of custom services."

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 5344
jira: SMILE-7324
title: "Previously, issuing an expunge operation for resources on a specific partition would fail. This problem has been fixed."

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5349
title: "Removed duplicate helperSvc bean in JpaConfig (also defined in imported MdmJpaConfig) to resolve BeanDefinitionOverrideException"

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5353
jira: SMILE-7451
title: "Previously, when using revincludes and includes with iterate, while also using revincludes without iterate,
the result omitted some resources that should have been included. This issue has now been fixed."

View File

@ -0,0 +1,8 @@
---
type: add
issue: 5355
title: "The generated OpenAPI documentation produced by OpenApiInterceptor will now include
additional details in the individual resource type documentation, including the values of
*CapabilityStatement.rest.resource.documentation*,
*CapabilityStatement.rest.resource.profile*, and
*CapabilityStatement.rest.resource.supportedProfile*."

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5356
title: "Clinical reasoning bug that did not invalidate resources in repository api global caches for terminology and libraries when updates/deletes were made."

View File

@ -8,4 +8,8 @@
<li>Flexmark (All): 0.50.40 -&gt; 0.64.8</li>
<li>Logback (All): 1.4.4 -&gt; 1.4.7</li>
<li>H2 Database (JPA): 2.1.214 -&gt; 2.2.220</li>
<li>Thymeleaf (Testpage Overlay): 3.0.14.RELEASE -&gt; 3.1.2.RELEASE</li>
<li>xpp3 (All): 1.1.4c.0 -&gt; 1.1.6</li>
<li>HtmlUnit (All): 2.67.0 -&gt; 2.70.0</li>
<li>org.hl7.fhir.core (All): 6.0.22.2 -&gt; 6.1.2</li>
</ul>"

View File

@ -0,0 +1,26 @@
This release introduces significant a change to the mechanism performing submission of resource modification events
to the message broker. Previously, an event would be submitted as part of the synchronous transaction
modifying a resource. Synchronous submission yielded responsive publishing with the caveat that events would be dropped
upon submission failure.
We have replaced the synchronous mechanism with a two stage process. Events are initially stored in
database upon completion of the transaction and subsequently submitted to the broker by a scheduled task.
This new asynchronous submission mechanism will introduce a slight delay in event publishing. It is our view that such
delay is largely compensated by the capability to retry submission upon failure which will eliminate event losses.
There are some potentially breaking changes:
* On resource retrieval and before storage, tags, security label and profile collections in resource meta will be
sorted in lexicographical order. The order of the elements for Coding types (i.e. tags and security labels) is defined
by the (security, code) pair of each element. This normally should not break any clients because these properties are
sets according to the FHIR specification, and hence the order of the elements in these collections should not matter.
Also with this change the following side effects can be observed:
- If using INLINE tag storage mode, the first update request to a resource which has tags, security
labels or profiles could create a superfluous resource version if the update request does not really introduce any
change to the resource. This is because the persisted tags, security labels, and profile may not be sorted in
lexicographical order, and this would be interpreted as a new resource version since the tags would be sorted
before storage after this change. If the update request actually changes the resource, there is no concern here.
Also, subsequent updates will not create an additional version because of ordering of the meta properties anymore.
- These meta collections are sorted in place by the storage layer before persisting the resource, so any piece of
code that is calling storage layer directly should not be passing in unmodifiable collections, as it would
result in an error.

View File

@ -1,3 +1,3 @@
---
release-date: "2022-02-18"
codename: "TBD"
codename: "Vishwa"

Some files were not shown because too many files have changed in this diff Show More