MVP EMPI implementation (#1857)

EMPI Initial Implementation (still plenty of known gaps)
This commit is contained in:
Tadgh 2020-05-22 12:03:20 -07:00 committed by GitHub
parent e22549e0c4
commit d1b963321a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
383 changed files with 14148 additions and 935 deletions

View File

@ -1,7 +1,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.1.0-SNAPSHOT</version>
@ -147,6 +147,10 @@
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
</ignoredDependencies>
<ignoredResourcePatterns>
<ignoredResourcePattern>.*\.txt$</ignoredResourcePattern>

View File

@ -27,6 +27,7 @@ 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.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.jena.riot.Lang;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
@ -36,8 +37,18 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/*
* #%L
@ -177,7 +188,12 @@ public class FhirContext {
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());
} else {
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
if ("true".equals(System.getProperty("unit_test_mode"))) {
String calledAt = ExceptionUtils.getStackFrames(new Throwable())[4];
ourLog.info("Creating new FHIR context for FHIR version [{}]{}", myVersion.getVersion().name(), calledAt);
} else {
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
}
}
myResourceTypesToScan = theResourceTypes;
@ -448,6 +464,36 @@ public class FhirContext {
}
/**
* Returns the name of a given resource class.
* @param theResourceType
* @return
*/
public String getResourceType(final Class<? extends IBaseResource> theResourceType) {
return getResourceDefinition(theResourceType).getName();
}
/**
* Returns the name of the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public String getResourceType(final IBaseResource theResource) {
return getResourceDefinition(theResource).getName();
}
/*
* Returns the type of the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
* <p>
* Note that this method is case insensitive!
* </p>
*
* @throws DataFormatException If the resource name is not known
*/
public String getResourceType(final String theResourceName) throws DataFormatException {
return getResourceDefinition(theResourceName).getName();
}
/*
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
* <p>
@ -476,7 +522,6 @@ public class FhirContext {
retVal = scanResourceType(clazz);
}
}
return retVal;
}
@ -501,8 +546,10 @@ public class FhirContext {
/**
* Returns an unmodifiable set containing all resource names known to this
* context
*
* @since 5.1.0
*/
public Set<String> getResourceNames() {
public Set<String> getResourceTypes() {
Set<String> resourceNames = new HashSet<>();
if (myNameToResourceDefinition.isEmpty()) {

View File

@ -306,7 +306,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
List<IBaseResource> resources = parseBundle(reader);
for (IBaseResource next : resources) {
String nextType = getFhirContext().getResourceDefinition(next).getName();
String nextType = getFhirContext().getResourceType(next);
if ("StructureDefinition".equals(nextType)) {
String url = getConformanceResourceUrl(next);

View File

@ -125,7 +125,7 @@ public interface IValidationSupport {
Validate.notNull(theClass, "theClass must not be null or blank");
Validate.notBlank(theUri, "theUri must not be null or blank");
switch (getFhirContext().getResourceDefinition(theClass).getName()) {
switch (getFhirContext().getResourceType(theClass)) {
case "StructureDefinition":
return theClass.cast(fetchStructureDefinition(theUri));
case "ValueSet":

View File

@ -20,15 +20,15 @@ package ca.uhn.fhir.fhirpath;
* #L%
*/
import org.hl7.fhir.instance.model.api.IBase;
import java.util.List;
import java.util.Optional;
import org.hl7.fhir.instance.model.api.IBase;
public interface IFhirPath {
/**
* Apply the given FluentPath expression against the given input and return
* Apply the given FhirPath expression against the given input and return
* all results in a list
*
* @param theInput The input object (generally a resource or datatype)
@ -38,7 +38,7 @@ public interface IFhirPath {
<T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType);
/**
* Apply the given FluentPath expression against the given input and return
* Apply the given FhirPath expression against the given input and return
* the first match (if any)
*
* @param theInput The input object (generally a resource or datatype)

View File

@ -757,6 +757,46 @@ public enum Pointcut {
*/
SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
* Invoked immediately after the delivery of MESSAGE subscription.
* <p>
* When this hook is called, all processing is complete so this hook should not
* make any changes to the parameters.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
SUBSCRIPTION_AFTER_MESSAGE_DELIVERY(void.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
* Invoked immediately before the delivery of a MESSAGE subscription.
* <p>
* Hooks may make changes to the delivery payload, or make changes to the
* canonical subscription such as adding headers, modifying the channel
* endpoint, etc.
* </p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription</li>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage</li>
* </ul>
* <p>
* Hooks may return <code>void</code> or may return a <code>boolean</code>. If the method returns
* <code>void</code> or <code>true</code>, processing will continue normally. If the method
* returns <code>false</code>, processing will be aborted.
* </p>
*/
SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY(boolean.class, "ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription", "ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage"),
/**
* <b>Subscription Hook:</b>
* Invoked whenever a persisted resource (a resource that has just been stored in the
@ -1522,6 +1562,23 @@ public enum Pointcut {
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"
),
/**
* <b>EMPI Hook:</b>
* Invoked whenever a persisted Patient/Practitioner resource (a resource that has just been stored in the
* database via a create/update/patch/etc.) has been matched against related resources and EMPI links have been updated.
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
* <li>ca.uhn.fhir.empi.model.TransactionLogMessages - This parameter is for informational messages provided by the EMPI module during EMPI procesing. .</li>
* </ul>
* </p>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
EMPI_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
/**
* <b>Performance Tracing Hook:</b>
* This hook is invoked when any informational messages generated by the

View File

@ -73,7 +73,7 @@ public abstract class BaseNarrativeGenerator implements INarrativeGenerator {
}
boolean retVal = false;
String resourceName = theFhirContext.getResourceDefinition(theResource).getName();
String resourceName = theFhirContext.getResourceType(theResource);
String contextPath = defaultIfEmpty(theTemplate.getContextPath(), resourceName);
// Narrative templates define a path within the resource that they apply to. Here, we're

View File

@ -445,7 +445,7 @@ public abstract class BaseParser implements IParser {
"This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
}
String resourceName = myContext.getResourceDefinition(theResource).getName();
String resourceName = myContext.getResourceType(theResource);
theEncodeContext.pushPath(resourceName, true);
doEncodeResourceToWriter(theResource, theWriter, theEncodeContext);
@ -995,7 +995,7 @@ public abstract class BaseParser implements IParser {
retVal = false;
} else {
if (myDontEncodeElements != null) {
String resourceName = myContext.getResourceDefinition(theResource).getName();
String resourceName = myContext.getResourceType(theResource);
if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath(resourceName + ".id"))) {
retVal = false;
} else if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath("*.id"))) {
@ -1020,7 +1020,7 @@ public abstract class BaseParser implements IParser {
*/
protected boolean shouldEncodePath(IResource theResource, String thePath) {
if (myDontEncodeElements != null) {
String resourceName = myContext.getResourceDefinition(theResource).getName();
String resourceName = myContext.getResourceType(theResource);
if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath(resourceName + "." + thePath))) {
return false;
} else return myDontEncodeElements.stream().noneMatch(t -> t.equalsPath("*." + thePath));

View File

@ -625,7 +625,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
EncodeContext encodeContext = new EncodeContext();
String resourceName = myContext.getResourceDefinition(theResource).getName();
String resourceName = myContext.getResourceType(theResource);
encodeContext.pushPath(resourceName, true);
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
}

View File

@ -20,8 +20,18 @@ package ca.uhn.fhir.parser;
* #L%
*/
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IMutator;
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.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition;
import ca.uhn.fhir.context.RuntimeResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IIdentifiableElement;
@ -45,7 +55,22 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseElement;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IBaseXhtml;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
@ -1055,7 +1080,7 @@ class ParserState<T> {
}
private void stitchBundleCrossReferences() {
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
final boolean bundle = "Bundle".equals(myContext.getResourceType(myInstance));
if (bundle) {
FhirTerser t = myContext.newTerser();
@ -1078,7 +1103,7 @@ class ParserState<T> {
for (IBaseResource next : myGlobalResources) {
IIdType id = next.getIdElement();
if (id != null && !id.isEmpty()) {
String resName = myContext.getResourceDefinition(next).getName();
String resName = myContext.getResourceType(next);
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
idToResource.put(idType.getValueAsString(), next);
}
@ -1172,7 +1197,7 @@ class ParserState<T> {
IResource nextResource = (IResource) getCurrentElement();
String version = ResourceMetadataKeyEnum.VERSION.get(nextResource);
String resourceName = myContext.getResourceDefinition(nextResource).getName();
String resourceName = myContext.getResourceType(nextResource);
String bundleIdPart = nextResource.getId().getIdPart();
if (isNotBlank(bundleIdPart)) {
// if (isNotBlank(entryBaseUrl)) {
@ -1221,7 +1246,7 @@ class ParserState<T> {
if (getCurrentElement() instanceof IDomainResource) {
IDomainResource elem = (IDomainResource) getCurrentElement();
String resourceName = myContext.getResourceDefinition(elem).getName();
String resourceName = myContext.getResourceType(elem);
String versionId = elem.getMeta().getVersionId();
if (StringUtils.isBlank(elem.getIdElement().getIdPart())) {
// Resource has no ID

View File

@ -288,7 +288,7 @@ public class RDFParser extends BaseParser {
}
case RESOURCE: {
IBaseResource baseResource = (IBaseResource) element;
String resourceName = this.context.getResourceDefinition(baseResource).getName();
String resourceName = this.context.getResourceType(baseResource);
if (!super.shouldEncodeResource(resourceName)) {
break;
}

View File

@ -295,7 +295,7 @@ public class XmlParser extends BaseParser {
}
case RESOURCE: {
IBaseResource resource = (IBaseResource) theElement;
String resourceName = myContext.getResourceDefinition(resource).getName();
String resourceName = myContext.getResourceType(resource);
if (!super.shouldEncodeResource(resourceName)) {
break;
}

View File

@ -10,11 +10,9 @@ import ca.uhn.fhir.util.DateUtils;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;

View File

@ -687,7 +687,7 @@ public class FhirTerser {
IBaseResource nextTarget = nextValue.getResource();
nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
if (!nextTargetId.hasResourceType()) {
String resourceType = myContext.getResourceDefinition(nextTarget).getName();
String resourceType = myContext.getResourceType(nextTarget);
nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null);
}
nextRef = nextTargetId.getValue();

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.util;
package ca.uhn.fhir.util;
/*-
* #%L
* HAPI FHIR JPA Server
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%

View File

@ -25,7 +25,12 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.api.Constants;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -345,4 +345,61 @@ public class ParametersUtil {
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
}
public static List<String> getNamedParameterPartAsString(FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
return extractNamedParameterPartsAsString(theCtx, theParameters, thePartName, theParameterName);
}
// TODO KHS need to consolidate duplicated functionality that came in from different branches
private static List<String> extractNamedParameterPartsAsString(FhirContext theCtx, IBaseParameters theParameters, String thePartName, String theParameterName) {
List<IBase> parameterReps = getParameterReps(theCtx, theParameters);
List<String> retVal = new ArrayList<>();
for (IBase nextParameter : parameterReps) {
BaseRuntimeElementCompositeDefinition<?> nextParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
Optional<? extends IPrimitiveType<?>> nameValue = getNameValue(nextParameter, nextParameterDef);
if (!nameValue.isPresent() || !thePartName.equals(nameValue.get().getValueAsString())) {
continue;
}
BaseRuntimeChildDefinition partChild = nextParameterDef.getChildByName("part");
List<IBase> partValues = partChild.getAccessor().getValues(nextParameter);
for (IBase partValue : partValues) {
BaseRuntimeElementCompositeDefinition<?> partParameterDef = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(partValue.getClass());
Optional<? extends IPrimitiveType<?>> partNameValue = getNameValue(partValue, partParameterDef);
if (!partNameValue.isPresent() || !theParameterName.equals(partNameValue.get().getValueAsString())) {
continue;
}
BaseRuntimeChildDefinition valueChild = partParameterDef.getChildByName("value[x]");
List<IBase> valueValues = valueChild.getAccessor().getValues(partValue);
valueValues
.stream()
.filter(t -> t instanceof IPrimitiveType<?>)
.map(t -> ((IPrimitiveType<String>) t))
.map(t -> defaultIfBlank(t.getValueAsString(), null))
.filter(t -> t != null)
.forEach(retVal::add);
}
}
return retVal;
}
private static List<IBase> getParameterReps(FhirContext theCtx, IBaseParameters theParameters) {
Validate.notNull(theParameters, "theParameters must not be null");
RuntimeResourceDefinition resDef = theCtx.getResourceDefinition(theParameters.getClass());
BaseRuntimeChildDefinition parameterChild = resDef.getChildByName("parameter");
return parameterChild.getAccessor().getValues(theParameters);
}
private static Optional<? extends IPrimitiveType<?>> getNameValue(IBase nextParameter, BaseRuntimeElementCompositeDefinition<?> theNextParameterDef) {
BaseRuntimeChildDefinition nameChild = theNextParameterDef.getChildByName("name");
List<IBase> nameValues = nameChild.getAccessor().getValues(nextParameter);
return nameValues
.stream()
.filter(t -> t instanceof IPrimitiveType<?>)
.map(t -> ((IPrimitiveType<?>) t))
.findFirst();
}
}

View File

@ -45,7 +45,7 @@ public class ResourceReferenceInfo {
public ResourceReferenceInfo(FhirContext theContext, IBaseResource theOwningResource, List<String> thePathToElement, IBaseReference theElement) {
myContext = theContext;
myOwningResource = theContext.getResourceDefinition(theOwningResource).getName();
myOwningResource = theContext.getResourceType(theOwningResource);
myResource = theElement;
if (thePathToElement != null && !thePathToElement.isEmpty()) {

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.model.util;
package ca.uhn.fhir.util;
/*-
* #%L
* HAPI FHIR Model
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@ -24,7 +24,7 @@ import java.io.CharArrayWriter;
import java.text.Normalizer;
public class StringNormalizer {
public static String normalizeString(String theString) {
public static String normalizeStringForSearchIndexing(String theString) {
CharArrayWriter outBuffer = new CharArrayWriter(theString.length());
/*

View File

@ -23,6 +23,9 @@ import ca.uhn.fhir.rest.gclient.TokenClientParam;
* #L%
*/
/**
* An IBaseResource that has a FHIR version of DSTU3 or higher
*/
public interface IAnyResource extends IBaseResource {
/**

View File

@ -4,7 +4,8 @@ import org.junit.Test;
import java.time.LocalDate;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class RequestPartitionIdTest {

View File

@ -36,6 +36,11 @@
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-validation</artifactId>
@ -96,6 +101,11 @@
<artifactId>hapi-fhir-jpaserver-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-jpaserver-searchparam</artifactId>

View File

@ -129,7 +129,7 @@ public class ExampleDataUploader extends BaseCommand {
}
ourLog.info("Found example {} - {} - {} chars", nextEntry.getName(), parsed.getClass().getSimpleName(), exampleString.length());
if (ctx.getResourceDefinition(parsed).getName().equals("Bundle")) {
if (ctx.getResourceType(parsed).equals("Bundle")) {
BaseRuntimeChildDefinition entryChildDef = ctx.getResourceDefinition(parsed).getChildByName("entry");
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
@ -139,13 +139,13 @@ public class ExampleDataUploader extends BaseCommand {
continue;
}
for (IBase nextResource : resources) {
if (!ctx.getResourceDefinition(parsed).getName().equals("Bundle") && ctx.getResourceDefinition(parsed).getName().equals("SearchParameter")) {
if (!ctx.getResourceType(parsed).equals("Bundle") && ctx.getResourceType(parsed).equals("SearchParameter")) {
bundle.addEntry().setRequest(new EntryRequest().setMethod(HTTPVerbEnum.POST)).setResource((IResource) nextResource);
}
}
}
} else {
if (ctx.getResourceDefinition(parsed).getName().equals("SearchParameter")) {
if (ctx.getResourceType(parsed).equals("SearchParameter")) {
continue;
}
bundle.addEntry().setRequest(new EntryRequest().setMethod(HTTPVerbEnum.POST)).setResource((IResource) parsed);
@ -205,7 +205,7 @@ public class ExampleDataUploader extends BaseCommand {
continue;
}
if (ctx.getResourceDefinition(parsed).getName().equals("Bundle")) {
if (ctx.getResourceType(parsed).equals("Bundle")) {
BaseRuntimeChildDefinition entryChildDef = ctx.getResourceDefinition(parsed).getChildByName("entry");
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
@ -227,7 +227,7 @@ public class ExampleDataUploader extends BaseCommand {
}
}
} else {
if (ctx.getResourceDefinition(parsed).getName().equals("SearchParameter")) {
if (ctx.getResourceType(parsed).equals("SearchParameter")) {
continue;
}
BundleEntryComponent entry = bundle.addEntry();
@ -289,7 +289,7 @@ public class ExampleDataUploader extends BaseCommand {
continue;
}
if (ctx.getResourceDefinition(parsed).getName().equals("Bundle")) {
if (ctx.getResourceType(parsed).equals("Bundle")) {
BaseRuntimeChildDefinition entryChildDef = ctx.getResourceDefinition(parsed).getChildByName("entry");
BaseRuntimeElementCompositeDefinition<?> entryDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
@ -311,7 +311,7 @@ public class ExampleDataUploader extends BaseCommand {
}
}
} else {
if (ctx.getResourceDefinition(parsed).getName().equals("SearchParameter")) {
if (ctx.getResourceType(parsed).equals("SearchParameter")) {
continue;
}
org.hl7.fhir.r4.model.Bundle.BundleEntryComponent entry = bundle.addEntry();
@ -663,7 +663,7 @@ public class ExampleDataUploader extends BaseCommand {
for (Iterator<IBaseResource> iter = resources.iterator(); iter.hasNext(); ) {
IBaseResource next = iter.next();
String nextType = ctx.getResourceDefinition(next).getName();
String nextType = ctx.getResourceType(next);
if (nextType.endsWith("Definition")) {
iter.remove();
} else if (nextType.contains("ValueSet")) {

View File

@ -36,7 +36,7 @@ public class LoadingValidationSupportDstu2 implements IValidationSupport {
@Override
public <T extends IBaseResource> T fetchResource(Class<T> theClass, String theUri) {
String resName = myCtx.getResourceDefinition(theClass).getName();
String resName = myCtx.getResourceType(theClass);
ourLog.info("Attempting to fetch {} at URL: {}", resName, theUri);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);

View File

@ -36,7 +36,7 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
@Override
public <T extends IBaseResource> T fetchResource(Class<T> theClass, String theUri) {
String resName = myCtx.getResourceDefinition(theClass).getName();
String resName = myCtx.getResourceType(theClass);
ourLog.info("Attempting to fetch {} at URL: {}", resName, theUri);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);

View File

@ -34,7 +34,7 @@ public class LoadingValidationSupportR4 implements IValidationSupport {
@Override
public <T extends IBaseResource> T fetchResource(Class<T> theClass, String theUri) {
String resName = myCtx.getResourceDefinition(theClass).getName();
String resName = myCtx.getResourceType(theClass);
ourLog.info("Attempting to fetch {} at URL: {}", resName, theUri);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);

View File

@ -26,7 +26,9 @@ import java.util.List;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class HapiFlywayMigrateDatabaseCommandTest {
@ -37,7 +39,6 @@ public class HapiFlywayMigrateDatabaseCommandTest {
System.setProperty("test", "true");
}
// TODO INTERMITTENT This just failed for me on CI with a BadSqlGrammarException
@Test
public void testMigrateFrom340() throws IOException, SQLException {

View File

@ -22,7 +22,11 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -31,7 +35,6 @@ import com.google.common.base.Charsets;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.IdType;
import java.net.URI;
import java.net.URISyntaxException;

View File

@ -22,7 +22,11 @@ package ca.uhn.fhir.cli;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -31,7 +35,6 @@ import com.google.common.base.Charsets;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.IdType;
import java.net.URI;
import java.net.URISyntaxException;

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.demo;
* #L%
*/
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;

View File

@ -23,10 +23,10 @@ package ca.uhn.fhir.jpa.demo;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
@ -36,7 +36,6 @@ import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.api.rp.ResourceProviderFactory;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
@ -45,6 +44,7 @@ import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.okhttp.client;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.BaseHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import okhttp3.Call;
import okhttp3.Call.Factory;
import okhttp3.Request;
import okhttp3.RequestBody;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@ -25,16 +35,6 @@ import java.util.Map;
* #L%
*/
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.BaseHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import okhttp3.Call;
import okhttp3.Call.Factory;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* Adapter for building an OkHttp-specific request.
*

View File

@ -361,7 +361,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private String toResourceName(Class<? extends IBaseResource> theType) {
return myContext.getResourceDefinition(theType).getName();
return myContext.getResourceType(theType);
}
@Override
@ -787,7 +787,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType) {
Validate.notNull(theResourceType, "theResourceType can not be null");
myConditional = true;
myResourceType = myContext.getResourceDefinition(theResourceType).getName();
myResourceType = myContext.getResourceType(theResourceType);
return this;
}
@ -904,7 +904,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
String resourceName;
String id;
if (myType != null) {
resourceName = myContext.getResourceDefinition(myType).getName();
resourceName = myContext.getResourceType(myType);
id = null;
} else if (myId != null) {
resourceName = myId.getResourceType();
@ -1282,7 +1282,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
String id;
String version;
if (myType != null) {
resourceName = myContext.getResourceDefinition(myType).getName();
resourceName = myContext.getResourceType(myType);
id = null;
version = null;
} else if (myId != null) {
@ -1559,7 +1559,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IPatchWithQuery conditional(Class<? extends IBaseResource> theClass) {
Validate.notNull(theClass, "theClass must not be null");
String resourceType = myContext.getResourceDefinition(theClass).getName();
String resourceType = myContext.getResourceType(theClass);
return conditional(resourceType);
}

View File

@ -59,7 +59,7 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
throw new ConfigurationException("Unable to determine resource type for method: " + theMethod);
}
myResourceName = theContext.getResourceDefinition(myResourceType).getName();
myResourceName = theContext.getResourceType(myResourceType);
if (resourceParameter == null) {
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a resource parameter annotated with @" + ResourceParam.class.getSimpleName());

View File

@ -99,7 +99,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
// If we're returning an abstract type, that's ok
} else {
myResourceType = (Class<? extends IResource>) theReturnResourceType;
myResourceName = theContext.getResourceDefinition(myResourceType).getName();
myResourceName = theContext.getResourceType(myResourceType);
}
}
}

View File

@ -53,7 +53,7 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
@Override
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IBaseResource theResource) {
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(getContext().getResourceDefinition(theResource).getName());
urlExtension.append(getContext().getResourceType(theResource));
return new HttpPostClientInvocation(getContext(), theResource, urlExtension.toString());
}

View File

@ -64,7 +64,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
if (type != IBaseResource.class && type != IResource.class) {
myResourceName = theContext.getResourceDefinition(type).getName();
myResourceName = theContext.getResourceType(type);
} else {
myResourceName = null;
}

View File

@ -20,9 +20,6 @@ package ca.uhn.fhir.rest.client.method;
* #L%
*/
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@ -30,6 +27,9 @@ import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.UrlSourceEnum;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import java.util.List;
import java.util.Map;
public class HttpSimpleGetClientInvocation extends BaseHttpClientInvocation {
private final String myUrl;

View File

@ -160,7 +160,7 @@ public class MethodUtil {
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource,
String theResourceBody, Map<String, List<String>> theMatchParams) {
String resourceType = theContext.getResourceDefinition(theResource).getName();
String resourceType = theContext.getResourceType(theResource);
StringBuilder b = createUrl(resourceType, theMatchParams);
@ -188,7 +188,7 @@ public class MethodUtil {
public static HttpPutClientInvocation createUpdateInvocation(IBaseResource theResource, String theResourceBody,
IIdType theId, FhirContext theContext) {
String resourceName = theContext.getResourceDefinition(theResource).getName();
String resourceName = theContext.getResourceType(theResource);
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(resourceName);
urlBuilder.append('/');

View File

@ -83,10 +83,10 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
myName = theOperationName;
if (theReturnTypeFromRp != null) {
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
setResourceName(theContext.getResourceType(theReturnTypeFromRp));
} else {
if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
setResourceName(theContext.getResourceType(theOperationType));
} else {
setResourceName(null);
}

View File

@ -83,7 +83,7 @@ public class PatchMethodBinding extends BaseOutcomeReturningMethodBindingWithRes
@Override
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IBaseResource theResource) {
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(getContext().getResourceDefinition(theResource).getName());
urlExtension.append(getContext().getResourceType(theResource));
return new HttpPostClientInvocation(getContext(), theResource, urlExtension.toString());
}

View File

@ -29,7 +29,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
@ -72,7 +71,7 @@ public class ValidateMethodBindingDstu2Plus extends OperationMethodBinding {
IBaseParameters parameters = (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
ParametersUtil.addParameterToParameters(theContext, parameters, "resource", theResource);
String resourceName = theContext.getResourceDefinition(theResource).getName();
String resourceName = theContext.getResourceType(theResource);
String resourceId = theResource.getIdElement().getIdPart();
BaseHttpClientInvocation retVal = createOperationInvocation(theContext, resourceName, resourceId, null,Constants.EXTOP_VALIDATE, parameters, false);

View File

@ -0,0 +1,8 @@
---
- item:
issue: "1857"
type: "change"
title: "**Breaking Change**:
The method `FhirContext#getResourceNames()` has been renamed to `FhirContext#getResourceTypes()`. HAPI currently
goes back and forth between the two, but is consolidating on `Types`.
"

View File

@ -48,6 +48,11 @@ page.server_jpa.performance=Performance
page.server_jpa.upgrading=Upgrade Guide
page.server_jpa.diff=Diff Operation
section.server_jpa_empi.title=JPA Server: EMPI
page.server_jpa_empi.empi=Enterprise Master Patient Index
page.server_jpa_empi.empi_operations=EMPI Operations
page.server_jpa_empi.empi_settings=Enabling EMPI in HAPI FHIR
section.server_jpa_partitioning.title=JPA Server: Partitioning and Multitenancy
page.server_jpa_partitioning.partitioning=Partitioning and Multitenancy
page.server_jpa_partitioning.partitioning_management_operations=Partitioning Management Operations

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -12,8 +12,8 @@ When the $diff operation is invoked at the instance level (meaning it is invoked
## Parameters
* `[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_FROM_VERSION_PARAMETER}]]=[versionId]`: (*optional*) If specified, compare using this version as the source. If not specified, the immediately previous version will be compared.
* `[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_INCLUDE_META_PARAMETER}]]=true`: (*optional*) If specified, changes to Resource.meta will be included in the diff. This element is omitted by default.
* `[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_FROM_VERSION_PARAMETER}]]=[versionId]`: (*optional*) If specified, compare using this version as the source. If not specified, the immediately previous version will be compared.
* `[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_INCLUDE_META_PARAMETER}]]=true`: (*optional*) If specified, changes to Resource.meta will be included in the diff. This element is omitted by default.
To invoke:
@ -51,14 +51,14 @@ When the $diff operation is invoked at the instance level (meaning it is invoked
## Parameters
* `[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_FROM_PARAMETER}]]=[reference]`: Specifies the source of the comparison. The value must include a resource type and a resource ID, and can optionally include a version, e.g. `Patient/123` or `Patient/123/_history/2`.
* `[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_TO_PARAMETER}]]=[reference]`: Specifies the target of the comparison. The value must include a resource type and a resource ID, and can optionally include a version, e.g. `Patient/123` or `Patient/123/_history/2`.
* `[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_INCLUDE_META_PARAMETER}]]=true`: (*optional*) If specified, changes to Resource.meta will be included in the diff. This element is omitted by default.
* `[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_FROM_PARAMETER}]]=[reference]`: Specifies the source of the comparison. The value must include a resource type and a resource ID, and can optionally include a version, e.g. `Patient/123` or `Patient/123/_history/2`.
* `[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_TO_PARAMETER}]]=[reference]`: Specifies the target of the comparison. The value must include a resource type and a resource ID, and can optionally include a version, e.g. `Patient/123` or `Patient/123/_history/2`.
* `[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_INCLUDE_META_PARAMETER}]]=true`: (*optional*) If specified, changes to Resource.meta will be included in the diff. This element is omitted by default.
To invoke:
```http
GET http://fhir.example.com/baseR4/$diff?[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_FROM_PARAMETER}]]=Patient/1&[[${T(ca.uhn.fhir.jpa.model.util.ProviderConstants).DIFF_TO_PARAMETER}]]=Patient/2
GET http://fhir.example.com/baseR4/$diff?[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_FROM_PARAMETER}]]=Patient/1&[[${T(ca.uhn.fhir.rest.server.provider.ProviderConstants).DIFF_TO_PARAMETER}]]=Patient/2
```
The server will produce a response resembling the following:

View File

@ -0,0 +1,158 @@
# Enterprise Master Person Index (EMPI)
HAPI FHIR 5.0.0 introduced preliminary support for **EMPI**.
An EMPI allows for links to be created and maintained between different Patient and/or Practitioner resources. These links are used to indicate the fact that different Patient/Practitioner resources are known or believed to refer to the same actual (real world) person.
These links may be created and updated using different combinations of automatic linking as well as manual linking.
Note: The following sections describe linking between Patient and Person resources. The same information applies for linking between Practitioner and Person, but for readability it is not repeated.
## Working Example
The [JPA Server Starter](/hapi-fhir/docs/server_jpa/get_started.html) project contains a complete working example of the HAPI EMPI feature and documentation about how to enable and configure it. You may wish to browse its source to see how this works.
## Person linking in FHIR
Because HAPI EMPI is implemented on the HAPI JPA Server, it uses the FHIR model to represent roles and links. The following illustration shows an example of how these links work.
<a href="/hapi-fhir/docs/images/empi-links.svg"><img src="/hapi-fhir/docs/images/empi-links.svg" alt="EMPI links" style="margin-left: 15px; margin-bottom: 15px; width: 500px;" /></a>
There are several resources that are used:
* Patient - Represents the record of a person who receives healthcare services
* Person - Represents a master record with links to one or more Patient and/or Practitioner resources that belong to the same person
# Automatic Linking
With EMPI enabled, the basic default behavior of the EMPI is simply to create a new Person record for every Patient that is created such that there is a 1:1 relationship between them. Any relinking is then expected to be done manually (i.e. via the forthcoming empi operations).
In a typical configuration it is often desirable to have links be created automatically using matching rules. For example, you might decide that if a Patient shares the same name, gender, and date of birth as another Patient, you have at least a little confidence that they are the same Person.
This automatic linking is done via configurable matching rules that create a links between Patients and Persons. Based on the strength of the match configured in these rules, the link will be set to either POSSIBLE_MATCH or MATCHED.
## Design Principles
Below are some simplifying principles HAPI EMPI enforces to reduce complexity and ensure data integrity.
1. When EMPI is enabled on a HAPI FHIR server, any Person resource in the repository that has the "hapi-empi" tag is considered read-only via the FHIR endpoint. These Person resources are managed exclusively by HAPI EMPI. Users can only directly change them via special empi operations. In most cases, users will indirectly change them by creating and updating Patient and Practitioner ("Patient") resources. For the rest of this document, assume "Person" refers to a "hapi-empi" tagged Person resource.
1. Every Patient in the system has a MATCH link to at most one Person resource.
1. Every Patient resource in the system has a MATCH link to a Person resource unless that Patient has the "no-empi" tag or it has POSSIBLE_MATCH links pending review.
1. The HAPI EMPI rules define a single identifier system that holds the external enterprise id ("EID"). If a Patient has an external EID, then the Person it links to always has the same EID. If a patient has no EID when it arrives, the person created from this patient is given an internal EID.
1. A Person can have both an internal EID(auto-created by HAPI), and an external EID (provided by an external system).
1. Two different Person resources cannot have the same EID.
1. Patient resources are only ever compared to Person resources via this EID. For all other matches, Patient resources are only ever compared to Patient resources and Practitioner resources are only ever compared to Practitioner resources.
## Links
1. HAPI EMPI manages empi-link records ("links") that link a Patient resource to a Person resource. When these are created/updated by matching rules, the links are marked as AUTO. When these links are changed manually, they are marked as MANUAL.
1. Once a link has been manually assigned as NO_MATCH or MATCHED, the system will not change it.
1. When a new Patient resource is created/updated then it is compared to all other Patient resources in the repository. The outcome of each of these comparisons is either NO_MATCH, POSSIBLE_MATCH or MATCHED.
1. Whenever a MATCHED link is established between a Patient resource and a Person resource, that Patient is always added to that Person resource links. All MATCHED links have corresponding Person resource links and all Person resource links have corresponding MATCHED empi-link records. You can think of the fields of the empi-link records as extra meta-data associated with each Person.link.target.
### Possible rule match outcomes:
When a new Patient resource is compared with all other resources of that type in the repository, there are four possible cases:
* CASE 1: No MATCHED and no POSSIBLE_MATCHED outcomes -> a new Person resource is created and linked to that Patient as MATCHED. All fields are copied from the Patient to the Person. If the incoming resource has an EID, it is copied to the Person. Otherwise a new UUID is created and used as the internal EID.
* CASE 2: All of the MATCHED Patient resources are already linked to the same Person -> a new Link is created between the new Patient and that Person and is set to MATCHED.
* CASE 3: The MATCHED Patient resources link to more than one Person -> Mark all links as POSSIBLE_MATCHED. All other Person resources are marked as POSSIBLE_DUPLICATE of this first Person. These duplicates are manually reviewed later and either merged or marked as NO_MATCH and the system will no longer consider them as a POSSIBLE_DUPLICATE going forward. POSSIBLE_DUPLICATE is the only link type that can have a Person as both the source and target of the link.
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, empi-link records are created with POSSIBLE_MATCH outcome and await manual assignment to either NO_MATCH or MATCHED. Person resources are not changed.
# Rules
HAPI EMPI rules are managed via a single json document. This document contains a version. empi-links derived from these rules are marked with this version. The following configuration is stored in the rules:
* **resourceSearchParams**: These define fields which must have at least one exact match before two resources are considered for matching. This is like a list of "pre-searches" that find potential candidates for matches, to avoid the expensive operation of running a match score calculation on all resources in the system. E.g. you may only wish to consider matching two Patients if they either share at least one identifier in common or have the same birthday.
```json
[ {
"resourceType" : "Patient",
"searchParam" : "birthdate"
}, {
"resourceType" : "Patient",
"searchParam" : "identifier"
} ]
```
* **filterSearchParams** When searching for match candidates, only resources that match this filter are considered. E.g. you may wish to only search for Patients for which active=true.
```json
[ {
"resourceType" : "Patient",
"searchParam" : "active",
"fixedValue" : "true"
} ]
```
* **matchFields** Once the match candidates have been found, they are then each assigned a match vector that marks which fields match. The match vector is determined by a list of matchFields. Each matchField defines a name, distance metric, a success threshold, a resource type, and resource path to check. For example:
```json
{
"name" : "given-name-cosine",
"resourceType" : "Patient",
"resourcePath" : "name.given",
"metric" : "COSINE",
"matchThreshold" : 0.8
}
```
Note that in all the above json, valid options for `resourceType` are `Patient`, `Practitioner`, and `All`. Use `All` if the criteria is identical across both resource types, and you would like to apply the pre-search to both practitioners and patients.
The following metrics are currently supported:
* JARO_WINKLER
* COSINE
* JACCARD
* NORMALIZED_LEVENSCHTEIN
* SORENSEN_DICE
* STANDARD_NAME_ANY_ORDER
* EXACT_NAME_ANY_ORDER
* STANDARD_NAME_FIRST_AND_LAST
* EXACT_NAME_FIRST_AND_LAST
See [java-string-similarity](https://github.com/tdebatty/java-string-similarity) for a description of the first five metrics. For the last four, STANDARd means ignore case and accents whereas EXACT must match casing and accents exactly. Name any order matches first and last names irrespective of order, whereas FIRST_AND_LAST metrics require the name match to be in order.
* **matchResultMap** A map which converts combinations of successful matchFields into an EMPI Match Result score for overall matching of a given pair of resources.
```json
"matchResultMap" : {
"given-name-cosine" : "POSSIBLE_MATCH",
"given-name-jaro, last-name-jaro" : "MATCH"
}
```
* **eidSystem**: The external EID system that the HAPI EMPI system should expect to see on incoming Patient resources. Must be a valid URI.
# Enterprise Identifiers
An Enterprise Identifier(EID) is a unique identifier that can be attached to Patients or Practitioners. Each implementation is expected to use exactly one EID system for incoming resources,
defined in the mentioned `empi-rules.json` file. If a Patient or Practitioner with a valid EID is added to the system, that EID will be copied over to the Person that was matched. In the case that
the incoming Patient or Practitioner had no EID assigned, an internal EID will be created for it. There are thus two classes of EID. Internal EIDs, created by HAPI-EMPI, and External EIDs, provided
by the install.
There are many edge cases for determining what will happen in merge and update scenarios, which will be provided in future documentation.
# HAPI EMPI Technical Details
When EMPI is enabled, the HAPI FHIR JPA Server does the following things on startup:
1. HAPI EMPI stores the extra link details in a table called `MPI_LINK`.
1. Each record in an `MPI_LINK` table corresponds to a `link.target` entry on a Person resource. HAPI EMPI uses the following convention for the Person.link.assurance level:
1. Level 1: not used
1. Level 2: POSSIBLE_MATCH
1. Level 3: AUTO MATCHED
1. Level 4: MANUAL MATCHED
1. It enables the MESSAGE subscription type and starts up the internal subscription engine.
1. It creates two MESSAGE subscriptions, called 'empi-patient' and 'empi-practitioner' that match all incoming Patient and Practitioner resources and send them to an internal queue called "empi". The JPA Server listens to this queue and links incoming resources to Persons.
1. It registers the `Patient/$match` operation. See [$match](https://www.hl7.org/fhir/operation-patient-match.html) for a description of this operation.
1. It registers a new dao interceptor that restricts access to EMPI managed Person records.

View File

@ -0,0 +1,362 @@
# EMPI Operations
Several operations exist that can be used to manage EMPI links. These operations are supplied by a [plain provider](/docs/server_plain/resource_providers.html#plain-providers) called [EmpiProvider](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/provider/EmpiProviderR4.html).
## Query links
Ue the `$empi-query-links` operation to view empi links. The results returned are based on the parameters provided. All parameters are optional. This operation takes the following parameters:
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>personId</td>
<td>String</td>
<td>0..1</td>
<td>
The id of the Person resource.
</td>
</tr>
<tr>
<td>targetId</td>
<td>String</td>
<td>0..1</td>
<td>
The id of the Patient or Practitioner resource.
</td>
</tr>
<tr>
<td>matchResult</td>
<td>String</td>
<td>0..1</td>
<td>
MATCH, POSSIBLE_MATCH or NO_MATCH.
</td>
</tr>
<tr>
<td>linkSource</td>
<td>String</td>
<td>0..1</td>
<td>
AUTO, MANUAL.
</td>
</tr>
</tbody>
</table>
### Example
Use an HTTP GET like `http://example.com/$empi-query-link?matchResult=POSSIBLE_MATCH` or an HTTP POST to the following URL to invoke this operation:
```url
http://example.com/$empi-query-link
```
The following request body could be used to find all POSSIBLE_MATCH links in the system:
```json
{
"resourceType": "Parameters",
"parameter": [ {
"name": "matchResult",
"valueString": "POSSIBLE_MATCH"
} ]
}
```
This operation returns a `Parameters` resource that looks like the following:
```json
<Parameters xmlns="http://hl7.org/fhir">
<parameter>
<name value="link"/>
<part>
<name value="personId"/>
<valueString value="Person/123"/>
</part>
<part>
<name value="targetId"/>
<valueString value="Patient/456"/>
</part>
<part>
<name value="matchResult"/>
<valueString value="MATCH"/>
</part>
<part>
<name value="linkSource"/>
<valueString value="AUTO"/>
</part>
</parameter>
</Parameters>
```
## Querying links via the Person resource
Alternatively, you can query Empi links by querying Person resources directly. Empi represents links in Person resources using the following mapping:
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>EMPI matchResult</th>
<th>EMPI linkSource</th>
<th>Person link.assurance</th>
</tr>
</thead>
<tbody>
<tr>
<td>NO_MATCH</td>
<td>MANUAL</td>
<td>No link present</td>
</tr>
<tr>
<td>POSSIBLE_MATCH</td>
<td>AUTO</td>
<td>level2</td>
</tr>
<tr>
<td>MATCH</td>
<td>AUTO</td>
<td>level3</td>
</tr>
<tr>
<td>MATCH</td>
<td>MANUAL</td>
<td>level4</td>
</tr>
</tbody>
</table>
For example, you can use the following HTTP GET to find all Person resources that have POSSIBLE_MATCH links:
```
http://example.com/Person?assurance=level2
```
## Query Duplicate Persons
Use the `$empi-duplicate-persons` operation to request a list of duplicate persons. This operation takes no parameters
### Example
Use an HTTP GET to the following URL to invoke this operation:
```url
http://example.com/$empi-duplicate-persons
```
This operation returns `Parameters` similar to `$empi-query-links`:
```json
<Parameters xmlns="http://hl7.org/fhir">
<parameter>
<name value="link"/>
<part>
<name value="personId"/>
<valueString value="Person/123"/>
</part>
<part>
<name value="targetId"/>
<valueString value="Person/789"/>
</part>
</parameter>
</Parameters>
```
## Update Link
Use the `$empi-update-link` operation to change the `matchResult` update of an empi link. This operation takes the following parameters:
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>personId</td>
<td>String</td>
<td>1..1</td>
<td>
The id of the Person resource.
</td>
</tr>
<tr>
<td>targetId</td>
<td>String</td>
<td>1..1</td>
<td>
The id of the Patient or Practitioner resource.
</td>
</tr>
<tr>
<td>matchResult</td>
<td>String</td>
<td>1..1</td>
<td>
Must be either MATCH or NO_MATCH.
</td>
</tr>
</tbody>
</table>
Empi links updated in this way will automatically have their `linkSource` set to `MANUAL`.
### Example
Use an HTTP POST to the following URL to invoke this operation:
```url
http://example.com/$empi-update-link
```
The following request body could be used:
```json
{
"resourceType": "Parameters",
"parameter": [ {
"name": "personId",
"valueString": "Person/123"
}, {
"name": "targetId",
"valueString": "Patient/456"
}, {
"name": "matchResult",
"valueString": "MATCH"
} ]
}
```
The operation returns the updated `Person` resource. Note that this is the only way to modify EMPI-managed `Person` resources.
## Merge Persons
The `$empi-merge-persons` operation can be used to merge one Person resource with another. When doing this, you will need to decide which resource to delete and which one to keep. Data from the personToKeep will be given precedence over data in the personToDelete. This operation takes the following parameters:
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>personIdToDelete</td>
<td>String</td>
<td>1..1</td>
<td>
The id of the Person resource to merge data from. This resource will be deleted after the merge.
</td>
</tr>
<tr>
<td>personIdToKeep</td>
<td>String</td>
<td>1..1</td>
<td>
The id of the Person to merge data into.
</td>
</tr>
</tbody>
</table>
### Example
Use an HTTP POST to the following URL to invoke this operation:
```url
http://example.com/$empi-merge-persons
```
The following request body could be used:
```json
{
"resourceType": "Parameters",
"parameter": [ {
"name": "personIdToDelete",
"valueString": "Person/123"
}, {
"name": "personIdToKeep",
"valueString": "Patient/128"
} ]
}
```
This operation returns the merged Person resource.
# Querying The EMPI
When EMPI is enabled, the [$match operation](http://hl7.org/fhir/patient-operation-match.html) will be enabled on the JPA Server.
This operation allows a Patient resource to be submitted to the endpoint, and the system will attempt to find and return any Patient resources that match it according to the matching rules.
For example, the following request may be submitted:
```http
POST /Patient/$match
Content-Type: application/fhir+json; charset=UTF-8
{
"resourceType":"Parameters",
"parameter": [
{
"name":"resource",
"resource": {
"resourceType":"Patient",
"name": [
{ "family":"foo" }
]
}
}
]
}
```
This might result in a response such as the following:
```json
{
"resourceType": "Bundle",
"id": "0e712adc-6979-4875-bbe9-70b883a955b8",
"meta": {
"lastUpdated": "2019-06-06T22:46:43.809+03:30"
},
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "3",
"meta": {
"versionId": "1",
"lastUpdated": "2019-06-06T22:46:43.339+03:30"
},
"name": [
{
"family": "foo",
"given": [
"bar"
]
}
],
"birthDate": "2000-01-01"
}
}
]
}
```

View File

@ -0,0 +1,11 @@
# Enabling EMPI in HAPI FHIR
Follow these steps to enable EMPI on the server:
The [EmpiSettings](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html) bean contains configuration settings related to EMPI within the server. To enable Empi, the [setEnabled(boolean)](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setEnabled(boolean)) property should be enabled.
The following settings are enabled by default:
* **Prevent EID Updates** ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventEidUpdates(boolean))): If this is enabled, then once an EID is set on a resource, it cannot be changed. If disabled, patients may have their EID updated.
* **Prevent multiple EIDs**: ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventMultipleEids(boolean))): If this is enabled, then a resource cannot have more than one EID, and incoming resources that break this rule will be rejected.

View File

@ -31,6 +31,11 @@
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
@ -101,6 +106,11 @@
<artifactId>hapi-fhir-jpaserver-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-api</artifactId>

View File

@ -23,8 +23,6 @@ package ca.uhn.fhir.jpa.api.dao;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.api.IDaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.lang3.Validate;
@ -35,7 +33,14 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
@ -76,7 +81,6 @@ public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
myAppCtx = theApplicationContext;
}
public IFhirSystemDao getSystemDao() {
IFhirSystemDao retVal = mySystemDao;
if (retVal == null) {
@ -118,7 +122,7 @@ public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
@Nullable
public <T extends IBaseResource> IFhirResourceDao<T> getResourceDaoOrNull(Class<T> theResourceType) {
String resourceName = myContext.getResourceDefinition(theResourceType).getName();
String resourceName = myContext.getResourceType(theResourceType);
try {
return (IFhirResourceDao<T>) getResourceDao(resourceName);
} catch (InvalidRequestException e) {
@ -184,10 +188,10 @@ public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
List<String> supportedResourceNames = myResourceNameToResourceDao
.keySet()
.stream()
.map(t -> myContext.getResourceDefinition(t).getName())
.map(t -> myContext.getResourceType(t))
.sorted()
.collect(Collectors.toList());
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + myContext.getResourceDefinition(theClass).getName() + " - Can handle: " + supportedResourceNames);
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + myContext.getResourceType(theClass) + " - Can handle: " + supportedResourceNames);
}
return retVal;
}

View File

@ -1,5 +1,12 @@
package ca.uhn.fhir.jpa.api.dao;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateRangeParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.servlet.http.HttpServletRequest;
/*
@ -21,11 +28,6 @@ import javax.servlet.http.HttpServletRequest;
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateRangeParam;
public interface IFhirResourceDaoEncounter<T extends IBaseResource> extends IFhirResourceDao<T> {

View File

@ -19,9 +19,11 @@ package ca.uhn.fhir.jpa.api.dao;
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
public interface IFhirResourceDaoValueSet<T extends IBaseResource, CD, CC> extends IFhirResourceDao<T> {

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.api.dao;
* #L%
*/
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource;
import org.hl7.fhir.instance.model.api.IAnyResource;

View File

@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.api.model;
* #L%
*/
import java.util.List;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.MethodOutcome;
import java.util.List;
/**
* This class is a replacement for {@link DaoMethodOutcome} for delete operations,
* as they can perform their operation over multiple resources

View File

@ -1,4 +1,5 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@ -14,29 +15,29 @@
<name>HAPI FHIR JPA Server</name>
<dependencies>
<!--
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>${jackson.version}</version>
</dependency>
<!--
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
-->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
</dependency>
</dependency>
<dependency>
<groupId>net.sf.saxon</groupId>
@ -74,6 +75,11 @@
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
@ -210,9 +216,9 @@
<!-- Patch Dependencies -->
<dependency>
<groupId>io.dogote</groupId>
<artifactId>json-patch</artifactId>
</dependency>
<groupId>io.dogote</groupId>
<artifactId>json-patch</artifactId>
</dependency>
<dependency>
<groupId>com.github.dnault</groupId>
<artifactId>xml-patch</artifactId>
@ -229,9 +235,9 @@
For some reason JavaDoc crashed during site generation unless we have this dependency
-->
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<scope>provided</scope>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<scope>provided</scope>
</dependency>
<!--
@ -280,12 +286,12 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.apache.tomcat</groupId>
@ -293,7 +299,7 @@
<scope>test</scope>
</dependency>
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@ -380,8 +386,8 @@
<artifactId>javax.activation-api</artifactId>
</exclusion>
<!--<exclusion>-->
<!--<groupId>org.jboss.spec.javax.transaction</groupId>-->
<!--<artifactId>jboss-transaction-api_1.2_spec</artifactId>-->
<!--<groupId>org.jboss.spec.javax.transaction</groupId>-->
<!--<artifactId>jboss-transaction-api_1.2_spec</artifactId>-->
<!--</exclusion>-->
</exclusions>
</dependency>
@ -581,8 +587,7 @@
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</dependency>
</dependencies>
</dependencies>
<properties>
@ -640,14 +645,14 @@
<version>${jaxb_runtime_version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-core</artifactId>-->
<!--<version>${jaxb_core_version}</version>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-core</artifactId>-->
<!--<version>${jaxb_core_version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-impl</artifactId>-->
<!--<version>${jaxb_core_version}</version>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-impl</artifactId>-->
<!--<version>${jaxb_core_version}</version>-->
<!--</dependency>-->
</dependencies>
</plugin>
@ -690,7 +695,8 @@
<version>dstu2</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.dstu2</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu2.xml</targetResourceSpringBeansFile>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu2.xml
</targetResourceSpringBeansFile>
<baseResourceNames/>
<excludeResourceNames>
<!-- <excludeResourceName>OperationDefinition</excludeResourceName> <excludeResourceName>OperationOutcome</excludeResourceName> -->
@ -706,7 +712,8 @@
<version>dstu3</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.dstu3</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu3.xml</targetResourceSpringBeansFile>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu3.xml
</targetResourceSpringBeansFile>
<baseResourceNames></baseResourceNames>
<excludeResourceNames>
</excludeResourceNames>
@ -721,7 +728,8 @@
<version>r4</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.r4</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-r4.xml</targetResourceSpringBeansFile>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-r4.xml
</targetResourceSpringBeansFile>
<baseResourceNames></baseResourceNames>
<excludeResourceNames>
</excludeResourceNames>
@ -736,7 +744,8 @@
<version>r5</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.r5</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-r5.xml</targetResourceSpringBeansFile>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-r5.xml
</targetResourceSpringBeansFile>
<baseResourceNames></baseResourceNames>
</configuration>
</execution>
@ -868,6 +877,6 @@
</plugins>
</build>
</profile>
</profiles>
</profiles>
</project>

View File

@ -223,7 +223,7 @@ public class BinaryAccessProvider {
@Nonnull
private IBinaryTarget findAttachmentForRequest(IBaseResource theResource, String thePath, ServletRequestDetails theRequestDetails) {
Optional<IBase> type = myCtx.newFluentPath().evaluateFirst(theResource, thePath, IBase.class);
String resType = this.myCtx.getResourceDefinition(theResource).getName();
String resType = this.myCtx.getResourceType(theResource);
if (!type.isPresent()) {
String msg = this.myCtx.getLocalizer().getMessageSanitized(BinaryAccessProvider.class, "unknownPath", resType, thePath);
throw new InvalidRequestException(msg);

View File

@ -181,7 +181,7 @@ public class BinaryStorageInterceptor {
IIdType resourceId = theResource.getIdElement();
if (!resourceId.hasResourceType() && resourceId.hasIdPart()) {
String resourceType = myCtx.getResourceDefinition(theResource).getName();
String resourceType = myCtx.getResourceType(theResource);
resourceId = new IdType(resourceType + "/" + resourceId.getIdPart());
}

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.bulk;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.util.JsonUtil;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.Constants;
@ -31,6 +30,7 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ArrayUtil;
import ca.uhn.fhir.util.JsonUtil;
import ca.uhn.fhir.util.OperationOutcomeUtil;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;

View File

@ -378,7 +378,7 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc {
// This is probably not a useful default, but having the default be "download the whole
// server" seems like a risky default too. We'll deal with that by having the default involve
// only returning a small time span
resourceTypes = myContext.getResourceNames();
resourceTypes = myContext.getResourceTypes();
if (since == null) {
since = DateUtils.addDays(new Date(), -1);
}

View File

@ -103,7 +103,8 @@ import java.util.Date;
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*\\.test\\..*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.subscription.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.searchparam.*")
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.searchparam.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.empi.*")
})
@Import({
SearchParamConfig.class
@ -122,6 +123,8 @@ public abstract class BaseConfig {
@Autowired
protected Environment myEnv;
@Autowired
private DaoRegistry myDaoRegistry;
@Bean("myDaoRegistry")
public DaoRegistry daoRegistry() {
@ -244,7 +247,7 @@ public abstract class BaseConfig {
* Subclasses may override
*/
protected boolean isSupported(String theResourceType) {
return daoRegistry().getResourceDaoOrNull(theResourceType) != null;
return myDaoRegistry.getResourceDaoOrNull(theResourceType) != null;
}
@Bean

View File

@ -7,7 +7,6 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.JpaPersistedResourceValidationSupport;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu2;
import ca.uhn.fhir.jpa.term.TermReadSvcDstu2;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.util.ResourceCountCache;

View File

@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl;
import ca.uhn.fhir.jpa.term.TermReadSvcDstu3;
import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcDstu3;

View File

@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl;
import ca.uhn.fhir.jpa.term.TermReadSvcR4;
import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR4;
@ -51,6 +50,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
public class BaseR4Config extends BaseConfigDstu3Plus {
public static FhirContext ourFhirContext = FhirContext.forR4();
@Override
public FhirContext fhirContext() {
return fhirContextR4();
@ -65,7 +66,7 @@ public class BaseR4Config extends BaseConfigDstu3Plus {
@Bean
@Primary
public FhirContext fhirContextR4() {
FhirContext retVal = FhirContext.forR4();
FhirContext retVal = ourFhirContext;
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();

View File

@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5;
import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl;
import ca.uhn.fhir.jpa.term.TermReadSvcR5;
import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR5;

View File

@ -964,11 +964,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
public String toResourceName(Class<? extends IBaseResource> theResourceType) {
return myContext.getResourceDefinition(theResourceType).getName();
return myContext.getResourceType(theResourceType);
}
String toResourceName(IBaseResource theResource) {
return myContext.getResourceDefinition(theResource).getName();
return myContext.getResourceType(theResource);
}
protected ResourceTable updateEntityForDelete(RequestDetails theRequest, TransactionDetails theTransactionDetails, ResourceTable entity) {
@ -997,7 +997,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
validateResourceForStorage((T) theResource, entity);
}
}
String resourceType = myContext.getResourceDefinition(theResource).getName();
String resourceType = myContext.getResourceType(theResource);
if (isNotBlank(entity.getResourceType()) && !entity.getResourceType().equals(resourceType)) {
throw new UnprocessableEntityException(
"Existing resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + entity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
@ -1346,7 +1346,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
allowAny = true;
break;
}
validTypes.add(getContext().getResourceDefinition(nextValidType).getName());
validTypes.add(getContext().getResourceType(nextValidType));
}
if (allowAny) {
@ -1417,7 +1417,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data");
}
String resName = getContext().getResourceDefinition(theResource).getName();
String resName = getContext().getResourceType(theResource);
validateChildReferences(theResource, resName);
validateMetaCount(totalMetaCount);

View File

@ -85,7 +85,7 @@ public abstract class BaseStorageDao {
* @param theResource The resource that is about to be stored
*/
protected void preProcessResourceForStorage(IBaseResource theResource) {
String type = getContext().getResourceDefinition(theResource).getName();
String type = getContext().getResourceType(theResource);
if (getResourceName() != null && !getResourceName().equals(type)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "incorrectResourceType", type, getResourceName()));
}

View File

@ -649,7 +649,7 @@ public abstract class BaseTransactionProcessor {
}
String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry);
String resourceType = res != null ? myContext.getResourceDefinition(res).getName() : null;
String resourceType = res != null ? myContext.getResourceType(res) : null;
Integer order = theOriginalRequestOrder.get(nextReqEntry);
IBase nextRespEntry = (IBase) myVersionAdapter.getEntries(theResponse).get(order);
@ -997,7 +997,7 @@ public abstract class BaseTransactionProcessor {
private String toResourceName(Class<? extends IBaseResource> theResourceType) {
return myContext.getResourceDefinition(theResourceType).getName();
return myContext.getResourceType(theResourceType);
}
public void setContext(FhirContext theContext) {

View File

@ -0,0 +1,195 @@
package ca.uhn.fhir.jpa.dao;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.EmpiTransactionContext;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Service
public class EmpiLinkDaoSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Autowired
private IEmpiLinkDao myEmpiLinkDao;
@Autowired
private IdHelperService myIdHelperService;
public EmpiLink createOrUpdateLinkEntity(IBaseResource thePerson, IBaseResource theTarget, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, @Nullable EmpiTransactionContext theEmpiTransactionContext) {
Long personPid = myIdHelperService.getPidOrNull(thePerson);
Long resourcePid = myIdHelperService.getPidOrNull(theTarget);
EmpiLink empiLink = getOrCreateEmpiLinkByPersonPidAndTargetPid(personPid, resourcePid);
empiLink.setLinkSource(theLinkSource);
empiLink.setMatchResult(theMatchResult);
String message = String.format("Creating EmpiLink from %s to %s -> %s", thePerson.getIdElement().toUnqualifiedVersionless(), theTarget.getIdElement().toUnqualifiedVersionless(), theMatchResult);
theEmpiTransactionContext.addTransactionLogMessage(message);
ourLog.debug(message);
save(empiLink);
return empiLink;
}
@Nonnull
public EmpiLink getOrCreateEmpiLinkByPersonPidAndTargetPid(Long thePersonPid, Long theResourcePid) {
Optional<EmpiLink> oExisting = getLinkByPersonPidAndTargetPid(thePersonPid, theResourcePid);
if (oExisting.isPresent()) {
return oExisting.get();
} else {
EmpiLink empiLink = new EmpiLink();
empiLink.setPersonPid(thePersonPid);
empiLink.setTargetPid(theResourcePid);
return empiLink;
}
}
public Optional<EmpiLink> getLinkByPersonPidAndTargetPid(Long thePersonPid, Long theTargetPid) {
if (theTargetPid == null || thePersonPid == null) {
return Optional.empty();
}
EmpiLink link = new EmpiLink();
link.setTargetPid(theTargetPid);
link.setPersonPid(thePersonPid);
Example<EmpiLink> example = Example.of(link);
return myEmpiLinkDao.findOne(example);
}
public List<EmpiLink> getEmpiLinksByTargetPidAndMatchResult(Long theTargetPid, EmpiMatchResultEnum theMatchResult) {
EmpiLink exampleLink = new EmpiLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
public Optional<EmpiLink> getMatchedLinkForTargetPid(Long theTargetPid) {
EmpiLink exampleLink = new EmpiLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(EmpiMatchResultEnum.MATCH);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
public Optional<EmpiLink> getMatchedLinkForTarget(IBaseResource theTarget) {
Long pid = myIdHelperService.getPidOrNull(theTarget);
if (pid == null) {
return Optional.empty();
}
EmpiLink exampleLink = new EmpiLink();
exampleLink.setTargetPid(pid);
exampleLink.setMatchResult(EmpiMatchResultEnum.MATCH);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
public Optional<EmpiLink> getEmpiLinksByPersonPidTargetPidAndMatchResult(Long thePersonPid, Long theTargetPid, EmpiMatchResultEnum theMatchResult) {
EmpiLink exampleLink = new EmpiLink();
exampleLink.setPersonPid(thePersonPid);
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
/**
* Get all {@link EmpiLink} which have {@link EmpiMatchResultEnum#POSSIBLE_DUPLICATE} as their match result.
*
* @return A list of EmpiLinks that hold potential duplicate persons.
*/
public List<EmpiLink> getPossibleDuplicates() {
EmpiLink exampleLink = new EmpiLink();
exampleLink.setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
public Optional<EmpiLink> findEmpiLinkByTarget(IBaseResource theTargetResource) {
@Nullable Long pid = myIdHelperService.getPidOrNull(theTargetResource);
if (pid == null) {
return Optional.empty();
}
EmpiLink empiLink = new EmpiLink().setTargetPid(pid);
Example<EmpiLink> example = Example.of(empiLink);
return myEmpiLinkDao.findOne(example);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteLink(EmpiLink theEmpiLink) {
myEmpiLinkDao.delete(theEmpiLink);
}
/**
* Delete all EmpiLink records with any reference to this resource. (Used by Expunge.)
* @param theResource
* @return the number of records deleted
*/
public int deleteWithAnyReferenceTo(IBaseResource theResource) {
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null);
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPid(pid);
if (removed > 0) {
ourLog.info("Removed {} EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
}
return removed;
}
public List<EmpiLink> findEmpiLinksByPersonId(IBaseResource thePersonResource) {
@Nullable Long pid = myIdHelperService.getPidOrNull(thePersonResource);
if (pid == null) {
return Collections.emptyList();
}
EmpiLink empiLink = new EmpiLink().setPersonPid(pid);
Example<EmpiLink> example = Example.of(empiLink);
return myEmpiLinkDao.findAll(example);
}
public EmpiLink save(EmpiLink theEmpiLink) {
if (theEmpiLink.getCreated() == null) {
theEmpiLink.setCreated(new Date());
}
theEmpiLink.setUpdated(new Date());
return myEmpiLinkDao.save(theEmpiLink);
}
public List<EmpiLink> findEmpiLinkByExample(Example<EmpiLink> theExampleLink) {
return myEmpiLinkDao.findAll(theExampleLink);
}
}

View File

@ -81,7 +81,18 @@ import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.TypedQuery;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -373,7 +384,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextReqEntry.getRequest().getMethod()));
}
String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
String resourceType = res != null ? getContext().getResourceType(res) : null;
Entry nextRespEntry = theResponse.getEntry().get(theOriginalRequestOrder.get(nextReqEntry));
switch (verb) {

View File

@ -105,7 +105,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
localReference = true;
}
String resourceName = myFhirContext.getResourceDefinition(theClass).getName();
String resourceName = myFhirContext.getResourceType(theClass);
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
if (localReference) {

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.dao.data;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.entity.EmpiLink;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface IEmpiLinkDao extends JpaRepository<EmpiLink, Long> {
@Modifying
@Query("DELETE FROM EmpiLink f WHERE myPersonPid = :pid OR myTargetPid = :pid")
int deleteWithAnyReferenceToPid(@Param("pid") Long thePid);
}

View File

@ -23,8 +23,40 @@ package ca.uhn.fhir.jpa.dao.expunge;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.entity.SearchInclude;
import ca.uhn.fhir.jpa.entity.SearchResult;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
@ -37,6 +69,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@ -64,7 +97,7 @@ public class ExpungeEverythingService {
myTxTemplate = new TransactionTemplate(myPlatformTransactionManager);
}
void expungeEverything(RequestDetails theRequest) {
public void expungeEverything(@Nullable RequestDetails theRequest) {
final AtomicInteger counter = new AtomicInteger();

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.dao.expunge;
* #L%
*/
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -35,8 +34,6 @@ import org.springframework.stereotype.Service;
public abstract class ExpungeService {
private static final Logger ourLog = LoggerFactory.getLogger(ExpungeService.class);
@Autowired
private DaoConfig myConfig;
@Autowired
private ExpungeEverythingService myExpungeEverythingService;
@Autowired

View File

@ -27,9 +27,9 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.IResourceLinkResolver;

View File

@ -28,11 +28,12 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.cross.ResourceLookup;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.github.benmanes.caffeine.cache.Cache;
@ -42,12 +43,17 @@ import com.google.common.collect.MultimapBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.stereotype.Service;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collection;
@ -60,6 +66,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -84,12 +91,14 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
@Service
public class IdHelperService {
private static final Logger ourLog = LoggerFactory.getLogger(IdHelperService.class);
private static final String RESOURCE_PID = "RESOURCE_PID";
@Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired(required = true)
@Autowired
private DaoConfig myDaoConfig;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@ -107,7 +116,6 @@ public class IdHelperService {
myForcedIdCache = newCache();
}
public void delete(ForcedId forcedId) {
myForcedIdDao.deleteByPid(forcedId.getId());
}
@ -460,4 +468,48 @@ public class IdHelperService {
public static boolean isValidPid(String theIdPart) {
return StringUtils.isNumeric(theIdPart);
}
@Nullable
public Long getPidOrNull(IBaseResource theResource) {
IAnyResource anyResource = (IAnyResource) theResource;
Long retVal = (Long) anyResource.getUserData(RESOURCE_PID);
if (retVal == null) {
IIdType id = theResource.getIdElement();
try {
retVal = this.resolveResourcePersistentIds(null, id.getResourceType(), id.getIdPart()).getIdAsLong();
} catch (ResourceNotFoundException e) {
return null;
}
}
return retVal;
}
@Nonnull
public Long getPidOrThrowException(IIdType theId) {
return getPidOrThrowException(theId, null);
}
@Nonnull
public Long getPidOrThrowException(IAnyResource theResource) {
return (Long) theResource.getUserData(RESOURCE_PID);
}
@Nonnull
public Long getPidOrThrowException(IIdType theId, RequestDetails theRequestDetails) {
List<IIdType> ids = Collections.singletonList(theId);
List<ResourcePersistentId> resourcePersistentIds = this.resolveResourcePersistentIdsWithCache(RequestPartitionId.allPartitions(), ids);
return resourcePersistentIds.get(0).getIdAsLong();
}
public Map<Long, IIdType> getPidToIdMap(Collection<IIdType> theIds, RequestDetails theRequestDetails) {
return theIds.stream().collect(Collectors.toMap(t->getPidOrThrowException(t), Function.identity()));
}
public IIdType resourceIdFromPidOrThrowException(Long thePid) {
Optional<ResourceTable> optionalResource = myResourceTableDao.findById(thePid);
if (!optionalResource.isPresent()) {
throw new ResourceNotFoundException("Requested resource not found");
}
return optionalResource.get().getIdDt().toVersionless();
}
}

View File

@ -24,13 +24,13 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.StringNormalizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@ -131,7 +131,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
}
if (myDontUseHashesForSearch) {
String likeExpression = StringNormalizer.normalizeString(rawSearchTerm);
String likeExpression = StringNormalizer.normalizeStringForSearchIndexing(rawSearchTerm);
if (myDaoConfig.isAllowContainsSearches()) {
if (theParameter instanceof StringParam) {
if (((StringParam) theParameter).isContains()) {
@ -161,7 +161,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
return theBuilder.equal(theFrom.get("myHashExact").as(Long.class), hash);
} else {
// Normalized Match
String normalizedString = StringNormalizer.normalizeString(rawSearchTerm);
String normalizedString = StringNormalizer.normalizeStringForSearchIndexing(rawSearchTerm);
String likeExpression;
if ((theParameter instanceof StringParam) &&
(((((StringParam) theParameter).isContains()) &&

View File

@ -24,17 +24,17 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.api.model.DeleteConflict;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.OperationOutcomeUtil;

View File

@ -0,0 +1,205 @@
package ca.uhn.fhir.jpa.entity;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.annotations.OptimisticLock;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import java.util.Date;
@Entity
@Table(name = "MPI_LINK", uniqueConstraints = {
@UniqueConstraint(name = "IDX_EMPI_PERSON_TGT", columnNames = {"PERSON_PID", "TARGET_PID"}),
})
public class EmpiLink {
private static final int MATCH_RESULT_LENGTH = 16;
private static final int LINK_SOURCE_LENGTH = 16;
@SequenceGenerator(name = "SEQ_EMPI_LINK_ID", sequenceName = "SEQ_EMPI_LINK_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EMPI_LINK_ID")
@Id
@Column(name = "PID")
private Long myId;
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
@JoinColumn(name = "PERSON_PID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_EMPI_LINK_PERSON"), insertable=false, updatable=false, nullable=false)
private ResourceTable myPerson;
@Column(name = "PERSON_PID", nullable=false)
private Long myPersonPid;
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
@JoinColumn(name = "TARGET_PID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_EMPI_LINK_TARGET"), insertable=false, updatable=false, nullable=false)
private ResourceTable myTarget;
@Column(name = "TARGET_PID", updatable=false, nullable=false)
private Long myTargetPid;
@Column(name = "MATCH_RESULT", nullable = false)
@Enumerated(EnumType.ORDINAL)
private EmpiMatchResultEnum myMatchResult;
@Column(name = "LINK_SOURCE", nullable = false)
@Enumerated(EnumType.ORDINAL)
private EmpiLinkSourceEnum myLinkSource;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATED", nullable = false)
private Date myCreated;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "UPDATED", nullable = false)
private Date myUpdated;
public Long getId() {
return myId;
}
public EmpiLink setId(Long theId) {
myId = theId;
return this;
}
public ResourceTable getPerson() {
return myPerson;
}
public EmpiLink setPerson(ResourceTable thePerson) {
myPerson = thePerson;
myPersonPid = thePerson.getId();
return this;
}
public Long getPersonPid() {
return myPersonPid;
}
public EmpiLink setPersonPid(Long thePersonPid) {
myPersonPid = thePersonPid;
return this;
}
public ResourceTable getTarget() {
return myTarget;
}
public EmpiLink setTarget(ResourceTable theTarget) {
myTarget = theTarget;
myTargetPid = theTarget.getId();
return this;
}
public Long getTargetPid() {
return myTargetPid;
}
public EmpiLink setTargetPid(Long theTargetPid) {
myTargetPid = theTargetPid;
return this;
}
public EmpiMatchResultEnum getMatchResult() {
return myMatchResult;
}
public EmpiLink setMatchResult(EmpiMatchResultEnum theMatchResult) {
myMatchResult = theMatchResult;
return this;
}
public boolean isNoMatch() {
return myMatchResult == EmpiMatchResultEnum.NO_MATCH;
}
public boolean isMatch() {
return myMatchResult == EmpiMatchResultEnum.MATCH;
}
public boolean isPossibleMatch() {
return myMatchResult == EmpiMatchResultEnum.POSSIBLE_MATCH;
}
public EmpiLinkSourceEnum getLinkSource() {
return myLinkSource;
}
public EmpiLink setLinkSource(EmpiLinkSourceEnum theLinkSource) {
myLinkSource = theLinkSource;
return this;
}
public boolean isAuto() {
return myLinkSource == EmpiLinkSourceEnum.AUTO;
}
public boolean isManual() {
return myLinkSource == EmpiLinkSourceEnum.MANUAL;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("myId", myId)
.append("myPersonPid", myPersonPid)
.append("myTargetPid", myTargetPid)
.append("myMatchResult", myMatchResult)
.append("myLinkSource", myLinkSource)
.toString();
}
public Date getCreated() {
return myCreated;
}
public EmpiLink setCreated(Date theCreated) {
myCreated = theCreated;
return this;
}
public Date getUpdated() {
return myUpdated;
}
public EmpiLink setUpdated(Date theUpdated) {
myUpdated = theUpdated;
return this;
}
}

View File

@ -32,7 +32,14 @@ import ca.uhn.fhir.rest.api.Constants;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Subselect;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.Date;

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.jpa.partition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IPrimitiveType;

View File

@ -130,7 +130,7 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
.addIfMatchesType(ServletRequestDetails.class, theRequest);
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
String resourceName = myFhirContext.getResourceDefinition(theResource).getName();
String resourceName = myFhirContext.getResourceType(theResource);
validatePartition(requestPartitionId, resourceName, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE);
return normalizeAndNotifyHooks(requestPartitionId, theRequest);

View File

@ -2,9 +2,9 @@ package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.jpa.patch.FhirPatch;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
@ -31,6 +30,7 @@ import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import com.google.common.base.Objects;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -84,7 +84,6 @@ public class DiffProvider {
FhirPatch fhirPatch = newPatch(theIncludeMeta);
IBaseParameters diff = fhirPatch.diff(sourceResource, targetResource);
return diff;
}
@Operation(name = ProviderConstants.DIFF_OPERATION_NAME, idempotent = true)

View File

@ -23,8 +23,8 @@ package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;

View File

@ -22,13 +22,13 @@ package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc;
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;

View File

@ -22,8 +22,8 @@ package ca.uhn.fhir.jpa.provider.dstu3;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -22,8 +22,8 @@ package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -22,8 +22,8 @@ package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -110,7 +110,7 @@ public abstract class BaseSchedulerServiceImpl implements ISchedulerService, Sma
}
private IHapiScheduler createScheduler(boolean theClustered) throws SchedulerException {
if (!isLocalSchedulingEnabled() || isSchedulingDisabledForUnitTests()) {
if (isSchedulingDisabled()) {
ourLog.info("Scheduling is disabled on this server");
return new HapiNullScheduler();
}
@ -126,6 +126,10 @@ public abstract class BaseSchedulerServiceImpl implements ISchedulerService, Sma
return retval;
}
private boolean isSchedulingDisabled() {
return !isLocalSchedulingEnabled() || isSchedulingDisabledForUnitTests();
}
protected abstract IHapiScheduler getLocalHapiScheduler();
protected abstract IHapiScheduler getClusteredScheduler();
@ -193,6 +197,9 @@ public abstract class BaseSchedulerServiceImpl implements ISchedulerService, Sma
}
private void scheduleJob(String theInstanceName, IHapiScheduler theScheduler, long theIntervalMillis, ScheduledJobDefinition theJobDefinition) {
if (isSchedulingDisabled()) {
return;
}
ourLog.info("Scheduling {} job {} with interval {}", theInstanceName, theJobDefinition.getId(), StopWatch.formatMillis(theIntervalMillis));
if (theJobDefinition.getGroup() == null) {
theJobDefinition.setGroup(myDefaultGroup);

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.search;
*/
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -33,7 +33,6 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.dao.IResultIterator;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchInclude;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
@ -41,6 +40,7 @@ import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.jpa.search.reindex;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;

View File

@ -23,9 +23,9 @@ package ca.uhn.fhir.jpa.search.warm;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;

View File

@ -24,18 +24,44 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.ConceptValidationOptions;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IDao;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.api.model.TranslationQuery;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDesignationDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptPropertyDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptViewDao;
import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
import ca.uhn.fhir.jpa.entity.TermConceptPropertyFieldBridge;
import ca.uhn.fhir.jpa.entity.TermConceptPropertyTypeEnum;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptView;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
@ -47,6 +73,7 @@ import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
@ -81,7 +108,16 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.quartz.JobExecutionContext;
@ -110,7 +146,19 @@ import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

View File

@ -23,8 +23,8 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.IHapiJpaRepository;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
@ -74,7 +74,17 @@ import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

View File

@ -4,12 +4,12 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.IValueSetConceptAccumulator;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.util.VersionIndependentConcept;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseDatatype;

View File

@ -48,14 +48,14 @@ public class SubscriptionsRequireManualActivationInterceptorDstu2 extends Server
@Override
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
if (myDao.getContext().getResourceDefinition(theResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.CREATE, null, theResource);
}
}
@Override
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
if (myDao.getContext().getResourceDefinition(theNewResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theNewResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.UPDATE, theOldResource, theNewResource);
}
}

View File

@ -48,14 +48,14 @@ public class SubscriptionsRequireManualActivationInterceptorDstu3 extends Server
@Override
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
if (myDao.getContext().getResourceDefinition(theResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.CREATE, null, theResource);
}
}
@Override
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
if (myDao.getContext().getResourceDefinition(theNewResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theNewResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.UPDATE, theOldResource, theNewResource);
}
}

View File

@ -48,14 +48,14 @@ public class SubscriptionsRequireManualActivationInterceptorR4 extends ServerOpe
@Override
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
if (myDao.getContext().getResourceDefinition(theResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.CREATE, null, theResource);
}
}
@Override
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
if (myDao.getContext().getResourceDefinition(theNewResource).getName().equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
if (myDao.getContext().getResourceType(theNewResource).equals(ResourceTypeEnum.SUBSCRIPTION.getCode())) {
verifyStatusOk(RestOperationTypeEnum.UPDATE, theOldResource, theNewResource);
}
}

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