Add terminology delta operations (#1401)

* Start work on delta operations

* Add changelog

* Some build fixes

* Move upload terminology command to CodeSystem resource

* Updates

* Some test fixes

* Add changelog

* Some test fixes

* More test fixes

* Test fix

* Add additional tests

* Transaction boundary fixes
This commit is contained in:
James Agnew 2019-07-29 17:46:42 -04:00 committed by GitHub
parent 8158292665
commit a4ca5374ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 2527 additions and 1384 deletions

View File

@ -48,11 +48,11 @@ public class ExampleServlet extends ca.uhn.fhir.rest.server.RestfulServer {
*/
List<Object> plainProviders=new ArrayList<Object>();
plainProviders.add(new PlainProvider());
setPlainProviders(plainProviders);
registerProviders(plainProviders);
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
// ...add some resource providers...
setResourceProviders(resourceProviders);
registerProviders(resourceProviders);
}
}

View File

@ -264,6 +264,12 @@ public class ValidatorExamples {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
// TODO: implement
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
// TODO: implement

View File

@ -79,7 +79,7 @@ public class FhirContext {
private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
private ArrayList<Class<? extends IBase>> myCustomTypes;
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>();
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<>();
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private volatile boolean myInitialized;
private volatile boolean myInitializing = false;
@ -90,7 +90,7 @@ public class FhirContext {
private volatile INarrativeGenerator myNarrativeGenerator;
private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler();
private ParserOptions myParserOptions = new ParserOptions();
private Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<PerformanceOptionsEnum>();
private Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<>();
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
@ -198,7 +198,7 @@ public class FhirContext {
private void ensureCustomTypeList() {
myClassToElementDefinition.clear();
if (myCustomTypes == null) {
myCustomTypes = new ArrayList<Class<? extends IBase>>();
myCustomTypes = new ArrayList<>();
}
}
@ -278,14 +278,6 @@ public class FhirContext {
return myNameToElementDefinition.get(theElementName.toLowerCase());
}
/**
* For unit tests only
*/
int getElementDefinitionCount() {
validateInitialized();
return myClassToElementDefinition.size();
}
/**
* Returns all element definitions (resources, datatypes, etc.)
*/
@ -741,21 +733,21 @@ public class FhirContext {
}
private BaseRuntimeElementDefinition<?> scanDatatype(Class<? extends IElement> theResourceType) {
ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<Class<? extends IElement>>();
ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>();
resourceTypes.add(theResourceType);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
return defs.get(theResourceType);
}
private RuntimeResourceDefinition scanResourceType(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<Class<? extends IElement>>();
ArrayList<Class<? extends IElement>> resourceTypes = new ArrayList<>();
resourceTypes.add(theResourceType);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
return (RuntimeResourceDefinition) defs.get(theResourceType);
}
private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) {
List<Class<? extends IBase>> typesToScan = new ArrayList<Class<? extends IBase>>();
List<Class<? extends IBase>> typesToScan = new ArrayList<>();
if (theResourceTypes != null) {
typesToScan.addAll(theResourceTypes);
}
@ -769,7 +761,7 @@ public class FhirContext {
myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
}
Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>();
nameToElementDefinition.putAll(myNameToElementDefinition);
for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) {
if (!nameToElementDefinition.containsKey(next.getKey())) {
@ -777,7 +769,7 @@ public class FhirContext {
}
}
Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
Map<String, RuntimeResourceDefinition> nameToResourceDefinition = new HashMap<>();
nameToResourceDefinition.putAll(myNameToResourceDefinition);
for (Entry<String, RuntimeResourceDefinition> next : scanner.getNameToResourceDefinition().entrySet()) {
if (!nameToResourceDefinition.containsKey(next.getKey())) {
@ -785,7 +777,7 @@ public class FhirContext {
}
}
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> classToElementDefinition = new HashMap<>();
classToElementDefinition.putAll(myClassToElementDefinition);
classToElementDefinition.putAll(scanner.getClassToElementDefinitions());
for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) {
@ -798,7 +790,7 @@ public class FhirContext {
}
}
Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<String, RuntimeResourceDefinition>();
Map<String, RuntimeResourceDefinition> idToElementDefinition = new HashMap<>();
idToElementDefinition.putAll(myIdToResourceDefinition);
idToElementDefinition.putAll(scanner.getIdToResourceDefinition());
@ -864,9 +856,9 @@ public class FhirContext {
if (theResourceTypes == null) {
return null;
}
List<Class<? extends IElement>> resTypes = new ArrayList<Class<? extends IElement>>();
List<Class<? extends IElement>> resTypes = new ArrayList<>();
for (Class<? extends IBaseResource> next : theResourceTypes) {
resTypes.add((Class<? extends IElement>) next);
resTypes.add(next);
}
return resTypes;
}
@ -924,14 +916,14 @@ public class FhirContext {
}
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1);
retVal.add(theResourceType);
return retVal;
}
@SuppressWarnings("unchecked")
private static List<Class<? extends IBaseResource>> toCollection(Class<?>[] theResourceTypes) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1);
for (Class<?> clazz : theResourceTypes) {
if (!IResource.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(clazz.getCanonicalName() + " is not an instance of " + IResource.class.getSimpleName());

View File

@ -21,9 +21,21 @@ package ca.uhn.fhir.context.support;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This interface is a version-independent representation of the
@ -102,6 +114,68 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
*/
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
/**
* Look up a code using the system and code value
*
* @param theContext The FHIR context
* @param theSystem The CodeSystem URL
* @param theCode The code
*/
LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode);
class ConceptDesignation {
private String myLanguage;
private String myUseSystem;
private String myUseCode;
private String myUseDisplay;
private String myValue;
public String getLanguage() {
return myLanguage;
}
public ConceptDesignation setLanguage(String theLanguage) {
myLanguage = theLanguage;
return this;
}
public String getUseSystem() {
return myUseSystem;
}
public ConceptDesignation setUseSystem(String theUseSystem) {
myUseSystem = theUseSystem;
return this;
}
public String getUseCode() {
return myUseCode;
}
public ConceptDesignation setUseCode(String theUseCode) {
myUseCode = theUseCode;
return this;
}
public String getUseDisplay() {
return myUseDisplay;
}
public ConceptDesignation setUseDisplay(String theUseDisplay) {
myUseDisplay = theUseDisplay;
return this;
}
public String getValue() {
return myValue;
}
public ConceptDesignation setValue(String theValue) {
myValue = theValue;
return this;
}
}
abstract class BaseConceptProperty {
private final String myPropertyName;
@ -165,7 +239,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
}
}
class CodeValidationResult<CDCT, IST> {
abstract class CodeValidationResult<CDCT, IST> {
private CDCT myDefinition;
private String myMessage;
private IST mySeverity;
@ -228,6 +302,190 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return myDefinition != null;
}
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
LookupCodeResult retVal = new LookupCodeResult();
retVal.setSearchedForSystem(theSearchedForSystem);
retVal.setSearchedForCode(theSearchedForCode);
if (isOk()) {
retVal.setFound(true);
retVal.setCodeDisplay(getDisplay());
retVal.setCodeSystemDisplayName(getCodeSystemName());
retVal.setCodeSystemVersion(getCodeSystemVersion());
}
return retVal;
}
protected abstract String getDisplay();
}
class LookupCodeResult {
private String myCodeDisplay;
private boolean myCodeIsAbstract;
private String myCodeSystemDisplayName;
private String myCodeSystemVersion;
private boolean myFound;
private String mySearchedForCode;
private String mySearchedForSystem;
private List<IContextValidationSupport.BaseConceptProperty> myProperties;
private List<ConceptDesignation> myDesignations;
/**
* Constructor
*/
public LookupCodeResult() {
super();
}
public List<BaseConceptProperty> getProperties() {
if (myProperties == null) {
myProperties = new ArrayList<>();
}
return myProperties;
}
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) {
myProperties = theProperties;
}
@Nonnull
public List<ConceptDesignation> getDesignations() {
if (myDesignations == null) {
myDesignations = new ArrayList<>();
}
return myDesignations;
}
public String getCodeDisplay() {
return myCodeDisplay;
}
public void setCodeDisplay(String theCodeDisplay) {
myCodeDisplay = theCodeDisplay;
}
public String getCodeSystemDisplayName() {
return myCodeSystemDisplayName;
}
public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
myCodeSystemDisplayName = theCodeSystemDisplayName;
}
public String getCodeSystemVersion() {
return myCodeSystemVersion;
}
public void setCodeSystemVersion(String theCodeSystemVersion) {
myCodeSystemVersion = theCodeSystemVersion;
}
public String getSearchedForCode() {
return mySearchedForCode;
}
public LookupCodeResult setSearchedForCode(String theSearchedForCode) {
mySearchedForCode = theSearchedForCode;
return this;
}
public String getSearchedForSystem() {
return mySearchedForSystem;
}
public LookupCodeResult setSearchedForSystem(String theSearchedForSystem) {
mySearchedForSystem = theSearchedForSystem;
return this;
}
public boolean isCodeIsAbstract() {
return myCodeIsAbstract;
}
public void setCodeIsAbstract(boolean theCodeIsAbstract) {
myCodeIsAbstract = theCodeIsAbstract;
}
public boolean isFound() {
return myFound;
}
public LookupCodeResult setFound(boolean theFound) {
myFound = theFound;
return this;
}
public void throwNotFoundIfAppropriate() {
if (isFound() == false) {
throw new ResourceNotFoundException("Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]");
}
}
public IBaseParameters toParameters(FhirContext theContext, List<? extends IPrimitiveType<String>> theProperties) {
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
if (isNotBlank(getCodeSystemDisplayName())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "name", getCodeSystemDisplayName());
}
if (isNotBlank(getCodeSystemVersion())) {
ParametersUtil.addParameterToParametersString(theContext, retVal, "version", getCodeSystemVersion());
}
ParametersUtil.addParameterToParametersString(theContext, retVal, "display", getCodeDisplay());
ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "abstract", isCodeIsAbstract());
if (myProperties != null) {
Set<String> properties = Collections.emptySet();
if (theProperties != null) {
properties = theProperties
.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toSet());
}
for (IContextValidationSupport.BaseConceptProperty next : myProperties) {
if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) {
continue;
}
}
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
if (next instanceof IContextValidationSupport.StringConceptProperty) {
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next;
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) {
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next;
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
} else {
throw new IllegalStateException("Don't know how to handle " + next.getClass());
}
}
}
if (myDesignations != null) {
for (ConceptDesignation next : myDesignations) {
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
ParametersUtil.addPartCoding(theContext, property, "use", next.getUseSystem(), next.getUseCode(), next.getUseDisplay());
ParametersUtil.addPartString(theContext, property, "value", next.getValue());
}
}
return retVal;
}
public static LookupCodeResult notFound(String theSearchedForSystem, String theSearchedForCode) {
return new LookupCodeResult()
.setFound(false)
.setSearchedForSystem(theSearchedForSystem)
.setSearchedForCode(theSearchedForCode);
}
}
}

View File

@ -54,15 +54,31 @@ public @interface Operation {
String name();
/**
* On a client, this value should be populated with the resource type that the operation applies to. If set to
* This value may be populated with the resource type that the operation applies to. If set to
* {@link IBaseResource} (which is the default) than the operation applies to the server and not to a specific
* resource type.
* <p>
* This value has no effect when used on server implementations.
* This attribute should not be used a resource provider implementing
* <code>IResourceProvider</code> since the type can be inferred from the
* resource provider type.
* </p>
* @see #typeName() may also be used to specify a value as a String
*/
Class<? extends IBaseResource> type() default IBaseResource.class;
/**
* This value may be populated with the resource type that the operation applies to. If set to
* {@link IBaseResource} (which is the default) than the operation applies to the server and not to a specific
* resource type.
* <p>
* This attribute should not be used a resource provider implementing
* <code>IResourceProvider</code> since the type can be inferred from the
* resource provider type.
* </p>
* @see #type() may also be used to specify a value for this setting as a class type
*/
String typeName() default "";
/**
* If a given operation method is <b><a href="http://en.wikipedia.org/wiki/Idempotence">idempotent</a></b>
* (meaning roughly that it does not modify any data or state on the server)

View File

@ -37,31 +37,30 @@ public class AttachmentUtil {
*/
@SuppressWarnings("unchecked")
public static IPrimitiveType<byte[]> getOrCreateData(FhirContext theContext, ICompositeType theAttachment) {
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "data");
List<IBase> entries = entryChild.getAccessor().getValues(theAttachment);
return entries
.stream()
.map(t -> (IPrimitiveType<byte[]>) t)
.findFirst()
.orElseGet(() -> {
IPrimitiveType<byte[]> binary = newPrimitive(theContext, "base64Binary", null);
entryChild.getMutator().setValue(theAttachment, binary);
return binary;
});
return getOrCreateChild(theContext, theAttachment, "data", "base64Binary");
}
@SuppressWarnings("unchecked")
public static IPrimitiveType<String> getOrCreateContentType(FhirContext theContext, ICompositeType theAttachment) {
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "contentType");
return getOrCreateChild(theContext, theAttachment, "contentType", "string");
}
public static IPrimitiveType<String> getOrCreateUrl(FhirContext theContext, ICompositeType theAttachment) {
return getOrCreateChild(theContext, theAttachment, "url", "uri");
}
@SuppressWarnings("unchecked")
private static <T> IPrimitiveType<T> getOrCreateChild(FhirContext theContext, ICompositeType theAttachment, String theChildName, String theChildDatatype) {
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, theChildName);
List<IBase> entries = entryChild.getAccessor().getValues(theAttachment);
return entries
.stream()
.map(t -> (IPrimitiveType<String>) t)
.map(t -> (IPrimitiveType<T>) t)
.findFirst()
.orElseGet(() -> {
IPrimitiveType<String> string = newPrimitive(theContext, "string", null);
IPrimitiveType<String> string = newPrimitive(theContext, theChildDatatype, null);
entryChild.getMutator().setValue(theAttachment, string);
return string;
return (IPrimitiveType<T>) string;
});
}

View File

@ -61,8 +61,8 @@ public class ParametersUtil {
List<IBase> valueValues = valueChild.getAccessor().getValues(nextParameter);
valueValues
.stream()
.filter(t->t instanceof IPrimitiveType<?>)
.map(t->((IPrimitiveType<?>)t).getValueAsString())
.filter(t -> t instanceof IPrimitiveType<?>)
.map(t -> ((IPrimitiveType<?>) t).getValueAsString())
.filter(StringUtils::isNotBlank)
.forEach(retVal::add);
@ -107,11 +107,11 @@ public class ParametersUtil {
/**
* Add a paratemer value to a Parameters resource
*
* @param theContext The FhirContext
* @param theParameters The Parameters resource
* @param theName The parameter name
* @param theContext The FhirContext
* @param theParameters The Parameters resource
* @param theName The parameter name
* @param thePrimitiveDatatype The datatype, e.g. "string", or "uri"
* @param theValue The value
* @param theValue The value
*/
public static void addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName, String thePrimitiveDatatype, String theValue) {
Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty");
@ -142,9 +142,99 @@ public class ParametersUtil {
return value;
}
public static IPrimitiveType<?> createUri(FhirContext theContext, String theValue) {
IPrimitiveType<?> value = (IPrimitiveType<?>) theContext.getElementDefinition("uri").newInstance(theValue);
return value;
}
public static IPrimitiveType<?> createCode(FhirContext theContext, String theValue) {
IPrimitiveType<?> value = (IPrimitiveType<?>) theContext.getElementDefinition("code").newInstance(theValue);
return value;
}
public static IBaseParameters newInstance(FhirContext theContext) {
Validate.notNull(theContext, "theContext must not be null");
return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
}
@SuppressWarnings("unchecked")
public static void addParameterToParametersBoolean(FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) {
IPrimitiveType<Boolean> value = (IPrimitiveType<Boolean>) theCtx.getElementDefinition("boolean").newInstance();
value.setValue(theValue);
addParameterToParameters(theCtx, theParameters, theName, value);
}
@SuppressWarnings("unchecked")
public static void addParameterToParametersInteger(FhirContext theCtx, IBaseParameters theParameters, String theName, int theValue) {
IPrimitiveType<Integer> count = (IPrimitiveType<Integer>) theCtx.getElementDefinition("integer").newInstance();
count.setValue(theValue);
addParameterToParameters(theCtx, theParameters, theName, count);
}
public static void addParameterToParametersReference(FhirContext theCtx, IBaseParameters theParameters, String theName, String theReference) {
IBaseReference target = (IBaseReference) theCtx.getElementDefinition("reference").newInstance();
target.setReference(theReference);
addParameterToParameters(theCtx, theParameters, theName, target);
}
@SuppressWarnings("unchecked")
public static void addParameterToParametersString(FhirContext theCtx, IBaseParameters theParameters, String theName, String theValue) {
IPrimitiveType<String> value = (IPrimitiveType<String>) theCtx.getElementDefinition("string").newInstance();
value.setValue(theValue);
addParameterToParameters(theCtx, theParameters, theName, value);
}
/**
* Add a parameter with no value (typically because we'll be adding sub-parameters)
*/
public static IBase addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theParameters);
BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
return createParameterRepetition(theContext, theParameters, paramChild, paramChildElem, theName);
}
public static void addPartCode(FhirContext theContext, IBase theParameter, String theName, String theCode) {
IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("code").newInstance();
value.setValue(theCode);
addPart(theContext, theParameter, theName, value);
}
public static void addPartString(FhirContext theContext, IBase theParameter, String theName, String theValue) {
IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
value.setValue(theValue);
addPart(theContext, theParameter, theName, value);
}
public static void addPartCoding(FhirContext theContext, IBase theParameter, String theName, String theSystem, String theCode, String theDisplay) {
IBase coding = theContext.getElementDefinition("coding").newInstance();
BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(coding.getClass());
codingDef.getChildByName("system").getMutator().addValue(coding, createUri(theContext, theSystem));
codingDef.getChildByName("code").getMutator().addValue(coding, createCode(theContext, theCode));
codingDef.getChildByName("display").getMutator().addValue(coding, createString(theContext, theDisplay));
addPart(theContext, theParameter, theName, coding);
}
private static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
BaseRuntimeElementCompositeDefinition<?> partChildElem = (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
IBase part = partChildElem.newInstance();
partChild.getMutator().addValue(theParameter, part);
IPrimitiveType<String> name = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
name.setValue(theName);
partChildElem.getChildByName("name").getMutator().addValue(part, name);
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
}
}

View File

@ -95,6 +95,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null;

View File

@ -102,4 +102,9 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
}

View File

@ -29,6 +29,7 @@ import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.r4.model.CodeSystem;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -103,7 +104,7 @@ public class UploadTerminologyCommand extends BaseCommand {
ourLog.info("Beginning upload - This may take a while...");
IBaseParameters response = client
.operation()
.onServer()
.onType(CodeSystem.class)
.named(UPLOAD_EXTERNAL_CODE_SYSTEM)
.withParameters(inputParameters)
.execute();

View File

@ -127,6 +127,11 @@ public class IgPackValidationSupportDstu3 implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null;

View File

@ -27,6 +27,7 @@ import java.util.*;
import javax.annotation.PostConstruct;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.instance.hapi.validation.CachingValidationSupport;
@ -185,13 +186,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
return source;
}
private LookupCodeResult lookup(List<ExpansionContains> theContains, String theSystem, String theCode) {
private IContextValidationSupport.LookupCodeResult lookup(List<ExpansionContains> theContains, String theSystem, String theCode) {
for (ExpansionContains nextCode : theContains) {
String system = nextCode.getSystem();
String code = nextCode.getCode();
if (theSystem.equals(system) && theCode.equals(code)) {
LookupCodeResult retVal = new LookupCodeResult();
IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult();
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
retVal.setFound(true);
@ -210,7 +211,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
}
@Override
public LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, RequestDetails theRequest) {
public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CodingDt theCoding, RequestDetails theRequest) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
@ -236,13 +237,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null, theRequest);
List<ExpansionContains> contains = expansion.getExpansion().getContains();
LookupCodeResult result = lookup(contains, system, code);
IContextValidationSupport.LookupCodeResult result = lookup(contains, system, code);
if (result != null) {
return result;
}
}
LookupCodeResult retVal = new LookupCodeResult();
IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult();
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);

View File

@ -3,21 +3,15 @@ package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters;
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 org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import java.util.Collections;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
@ -43,7 +37,8 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext
List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest);
LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CD theCoding, RequestDetails theRequestDetails);
@Nonnull
IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, CD theCoding, RequestDetails theRequestDetails);
SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails);
@ -72,147 +67,4 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext
}
class LookupCodeResult {
private String myCodeDisplay;
private boolean myCodeIsAbstract;
private String myCodeSystemDisplayName;
private String myCodeSystemVersion;
private boolean myFound;
private String mySearchedForCode;
private String mySearchedForSystem;
private List<IContextValidationSupport.BaseConceptProperty> myProperties;
/**
* Constructor
*/
public LookupCodeResult() {
super();
}
public String getCodeDisplay() {
return myCodeDisplay;
}
public void setCodeDisplay(String theCodeDisplay) {
myCodeDisplay = theCodeDisplay;
}
public String getCodeSystemDisplayName() {
return myCodeSystemDisplayName;
}
public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
myCodeSystemDisplayName = theCodeSystemDisplayName;
}
public String getCodeSystemVersion() {
return myCodeSystemVersion;
}
public void setCodeSystemVersion(String theCodeSystemVersion) {
myCodeSystemVersion = theCodeSystemVersion;
}
public String getSearchedForCode() {
return mySearchedForCode;
}
public void setSearchedForCode(String theSearchedForCode) {
mySearchedForCode = theSearchedForCode;
}
public String getSearchedForSystem() {
return mySearchedForSystem;
}
public void setSearchedForSystem(String theSearchedForSystem) {
mySearchedForSystem = theSearchedForSystem;
}
public boolean isCodeIsAbstract() {
return myCodeIsAbstract;
}
public void setCodeIsAbstract(boolean theCodeIsAbstract) {
myCodeIsAbstract = theCodeIsAbstract;
}
public boolean isFound() {
return myFound;
}
public void setFound(boolean theFound) {
myFound = theFound;
}
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) {
myProperties = theProperties;
}
public void throwNotFoundIfAppropriate() {
if (isFound() == false) {
throw new ResourceNotFoundException("Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]");
}
}
public Parameters toParameters(List<? extends IPrimitiveType<String>> theProperties) {
Parameters retVal = new Parameters();
retVal.addParameter().setName("name").setValue(new StringType(getCodeSystemDisplayName()));
if (isNotBlank(getCodeSystemVersion())) {
retVal.addParameter().setName("version").setValue(new StringType(getCodeSystemVersion()));
}
retVal.addParameter().setName("display").setValue(new StringType(getCodeDisplay()));
retVal.addParameter().setName("abstract").setValue(new BooleanType(isCodeIsAbstract()));
if (myProperties != null) {
Set<String> properties = Collections.emptySet();
if (theProperties != null) {
properties = theProperties
.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toSet());
}
for (IContextValidationSupport.BaseConceptProperty next : myProperties) {
if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) {
continue;
}
}
Parameters.ParametersParameterComponent property = retVal.addParameter().setName("property");
property
.addPart()
.setName("code")
.setValue(new CodeType(next.getPropertyName()));
if (next instanceof IContextValidationSupport.StringConceptProperty) {
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next;
property
.addPart()
.setName("value")
.setValue(new StringType(prop.getValue()));
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) {
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next;
property
.addPart()
.setName("value")
.setValue(new Coding()
.setSystem(prop.getCodeSystem())
.setCode(prop.getCode())
.setDisplay(prop.getDisplay()));
} else {
throw new IllegalStateException("Don't know how to handle " + next.getClass());
}
}
}
return retVal;
}
}
}

View File

@ -24,8 +24,9 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
final class MetadataKeyResourcePid extends ResourceMetadataKeySupportingAnyResource<Long, Long> {
public final class MetadataKeyResourcePid extends ResourceMetadataKeySupportingAnyResource<Long, Long> {
private static final long serialVersionUID = 1L;
MetadataKeyResourcePid(String theValue) {

View File

@ -20,25 +20,19 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
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.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
@ -52,45 +46,18 @@ import java.util.Date;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class);
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired
private ITermCodeSystemDao myCsDao;
@Autowired
private ValidationSupportChain myValidationSupport;
// private LookupCodeResult lookup(List<ValueSetExpansionContainsComponent> theContains, String theSystem, String theCode) {
// for (ValueSetExpansionContainsComponent nextCode : theContains) {
//
// String system = nextCode.getSystem();
// String code = nextCode.getCode();
// if (theSystem.equals(system) && theCode.equals(code)) {
// LookupCodeResult retVal = new LookupCodeResult();
// retVal.setSearchedForCode(code);
// retVal.setSearchedForSystem(system);
// retVal.setFound(true);
// if (nextCode.getAbstractElement().getValue() != null) {
// retVal.setCodeIsAbstract(nextCode.getAbstractElement().booleanValue());
// }
// retVal.setCodeDisplay(nextCode.getDisplay());
// retVal.setCodeSystemVersion(nextCode.getVersion());
// retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement
// return retVal;
// }
//
// }
//
// return null;
// }
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
@ -103,7 +70,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
}
@Override
public LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
@ -128,40 +95,15 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
ourLog.debug("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(getContext(), system)) {
ourLog.debug("Code system {} is supported", system);
CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null);
IContextValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(getContext(), system, code);
if (result != null) {
if (result.isOk()) {
LookupCodeResult retVal = new LookupCodeResult();
retVal.setFound(true);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
retVal.setCodeDisplay(result.asConceptDefinition().getDisplay());
String codeSystemDisplayName = result.getCodeSystemName();
if (isBlank(codeSystemDisplayName)) {
codeSystemDisplayName = "Unknown";
}
retVal.setCodeSystemDisplayName(codeSystemDisplayName);
retVal.setCodeSystemVersion(result.getCodeSystemVersion());
retVal.setProperties(result.getProperties());
return retVal;
}
return result;
}
}
// We didn't find it..
LookupCodeResult retVal = new LookupCodeResult();
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
return retVal;
return IContextValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
@ -182,62 +124,17 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
}
}
private List<TermConcept> toPersistedConcepts(List<ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) {
ArrayList<TermConcept> retVal = new ArrayList<>();
for (ConceptDefinitionComponent next : theConcept) {
if (isNotBlank(next.getCode())) {
TermConcept termConcept = new TermConcept();
termConcept.setCode(next.getCode());
termConcept.setCodeSystemVersion(theCodeSystemVersion);
termConcept.setDisplay(next.getDisplay());
termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA);
retVal.add(termConcept);
for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) {
if (isNotBlank(designationComponent.getValue())) {
TermConceptDesignation designation = termConcept.addDesignation();
designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null);
if (designationComponent.hasUse()) {
designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null);
designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null);
designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null);
}
designation.setValue(designationComponent.getValue());
}
}
// TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here.
}
}
return retVal;
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
CodeSystem cs = (CodeSystem) theResource;
CodeSystem csDstu3 = (CodeSystem) theResource;
if (cs != null && isNotBlank(cs.getUrl())) {
String codeSystemUrl = cs.getUrl();
Long codeSystemResourcePid = retVal.getId();
org.hl7.fhir.r4.model.CodeSystem cs = VersionConvertor_30_40.convertCodeSystem(csDstu3);
addPidToResource(theEntity, cs);
if (retVal.getDeleted() != null) {
// deleting
} else if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) {
ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString());
TermCodeSystemVersion persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
ourLog.info("Code system has {} concepts", persCs.getConcepts().size());
myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs);
}
}
myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
return retVal;
}

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
@ -238,8 +238,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
}
// String code = theCode.getValue();
// String system = toStringOrNull(theSystem);
LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
if (result.isFound()) {
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
if (result != null && result.isFound()) {
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
return retVal;
}

View File

@ -171,7 +171,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
@Override
@Transactional(value = TxType.SUPPORTS)
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
return fetchCodeSystem(theCtx, theSystem) != null;
return false;
}
@Override
@ -193,6 +193,11 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null;

View File

@ -20,14 +20,10 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
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.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil;
@ -37,29 +33,25 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
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 org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4.class);
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired
private ITermCodeSystemDao myCsDao;
@Autowired
@ -76,8 +68,9 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i
return valueSetIds;
}
@Nonnull
@Override
public LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
@ -104,37 +97,15 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i
if (myValidationSupport.isCodeSystemSupported(getContext(), system)) {
ourLog.info("Code system {} is supported", system);
CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null);
if (result != null) {
if (result.isOk()) {
LookupCodeResult retVal = new LookupCodeResult();
retVal.setFound(true);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
retVal.setCodeDisplay(result.asConceptDefinition().getDisplay());
String codeSystemDisplayName = result.getCodeSystemName();
if (isBlank(codeSystemDisplayName)) {
codeSystemDisplayName = "Unknown";
}
retVal.setCodeSystemDisplayName(codeSystemDisplayName);
retVal.setCodeSystemVersion(result.getCodeSystemVersion());
retVal.setProperties(result.getProperties());
return retVal;
}
IContextValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(getContext(), system, code);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
LookupCodeResult retVal = new LookupCodeResult();
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
return retVal;
return IContextValidationSupport.LookupCodeResult.notFound(system, code);
}
@ -156,66 +127,15 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i
}
}
private List<TermConcept> toPersistedConcepts(List<ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) {
ArrayList<TermConcept> retVal = new ArrayList<>();
for (ConceptDefinitionComponent next : theConcept) {
if (isNotBlank(next.getCode())) {
TermConcept termConcept = new TermConcept();
termConcept.setCode(next.getCode());
termConcept.setCodeSystemVersion(theCodeSystemVersion);
termConcept.setDisplay(next.getDisplay());
termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA);
retVal.add(termConcept);
for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) {
if (isNotBlank(designationComponent.getValue())) {
TermConceptDesignation designation = termConcept.addDesignation();
designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null);
if (designationComponent.hasUse()) {
designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null);
designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null);
designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null);
}
designation.setValue(designationComponent.getValue());
}
}
// TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here.
}
}
return retVal;
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource);
if (cs != null && isNotBlank(cs.getUrl())) {
String codeSystemUrl = cs.getUrl();
if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) {
ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString());
Long codeSystemResourcePid = retVal.getId();
TermCodeSystemVersion persCs = myCsvDao.findCurrentVersionForCodeSystemResourcePid(codeSystemResourcePid);
if (persCs != null) {
ourLog.info("Code system version already exists in database");
} else {
persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
ourLog.info("Code system has {} concepts", persCs.getConcepts().size());
myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs);
}
}
}
myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
return retVal;
}

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
@ -239,7 +239,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4<ValueSet> imple
}
// String code = theCode.getValue();
// String system = toStringOrNull(theSystem);
LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
if (result.isFound()) {
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
return retVal;

View File

@ -173,7 +173,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
@Override
@Transactional(value = TxType.SUPPORTS)
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
return fetchCodeSystem(theCtx, theSystem) != null;
return false;
}
@Override
@ -195,6 +195,11 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
return null;

View File

@ -63,6 +63,13 @@ public class TermCodeSystem implements Serializable {
@Column(name = "CS_NAME", nullable = true, length = MAX_NAME_LENGTH)
private String myName;
/**
* Constructor
*/
public TermCodeSystem() {
super();
}
public String getCodeSystemUri() {
return myCodeSystemUri;
}

View File

@ -69,6 +69,9 @@ public class TermCodeSystemVersion implements Serializable {
@OneToOne(mappedBy = "myCurrentVersion", optional = true)
private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny;
@Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH)
private String myCodeSystemDisplayName;
/**
* Constructor
*/
@ -155,4 +158,14 @@ public class TermCodeSystemVersion implements Serializable {
return result;
}
public String getCodeSystemDisplayName() {
return myCodeSystemDisplayName;
}
public void setCodeSystemDisplayName(String theCodeSystemDisplayName) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(
theCodeSystemDisplayName, MAX_VERSION_LENGTH,
"Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemDisplayName));
myCodeSystemDisplayName = theCodeSystemDisplayName;
}
}

View File

@ -111,7 +111,7 @@ public class TermConcept implements Serializable {
setCode(theCode);
}
public TermConcept addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) {
public TermConceptParentChildLink addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) {
Validate.notNull(theRelationshipType, "theRelationshipType must not be null");
TermConceptParentChildLink link = new TermConceptParentChildLink();
link.setParent(this);
@ -120,7 +120,7 @@ public class TermConcept implements Serializable {
getChildren().add(link);
theChild.getParents().add(link);
return this;
return link;
}
public void addChildren(List<TermConcept> theChildren, RelationshipTypeEnum theRelationshipType) {

View File

@ -66,6 +66,14 @@ public class TermConceptProperty implements Serializable {
private String myValue;
@Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH)
private TermConceptPropertyTypeEnum myType;
/**
* Constructor
*/
public TermConceptProperty() {
super();
}
/**
* Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING}
*/

View File

@ -20,12 +20,8 @@ package ca.uhn.fhir.jpa.provider;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
@ -34,27 +30,33 @@ import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import javax.servlet.http.HttpServletRequest;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2<ValueSet> {
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
public ValueSet expand(
HttpServletRequest theServletRequest,
@IdParam(optional=true) IdDt theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter,
RequestDetails theRequestDetails) {
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdDt theId,
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
@OperationParam(name = "identifier", min = 0, max = 1) UriDt theIdentifier,
@OperationParam(name = "filter", min = 0, max = 1) StringDt theFilter,
RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue());
boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false;
if (!haveId && !haveIdentifier && !haveValueSet) {
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request");
}
@ -62,7 +64,7 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
throw new InvalidRequestException("$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
}
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
@ -73,51 +75,35 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
} else {
return dao.expand(theValueSet, toFilterString(theFilter));
}
} finally {
endRequest(theServletRequest);
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
private String toFilterString(StringDt theFilter) {
return theFilter != null ? theFilter.getValue() : null;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringDt.class, min=1),
@OperationParam(name="version", type=StringDt.class, min=0),
@OperationParam(name="display", type=StringDt.class, min=1),
@OperationParam(name="abstract", type=BooleanDt.class, min=1),
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = {
@OperationParam(name = "name", type = StringDt.class, min = 1),
@OperationParam(name = "version", type = StringDt.class, min = 0),
@OperationParam(name = "display", type = StringDt.class, min = 1),
@OperationParam(name = "abstract", type = BooleanDt.class, min = 1),
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeDt theCode,
@OperationParam(name="system", min=0, max=1) UriDt theSystem,
@OperationParam(name="coding", min=0, max=1) CodingDt theCoding,
RequestDetails theRequestDetails
) {
HttpServletRequest theServletRequest,
@OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt>) getDao();
LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
if (result.isFound()==false) {
IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
if (result.isFound() == false) {
throw new ResourceNotFoundException("Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]");
}
Parameters retVal = new Parameters();
@ -126,30 +112,29 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
retVal.addParameter().setName("version").setValue(new StringDt(result.getCodeSystemVersion()));
}
retVal.addParameter().setName("display").setValue(new StringDt(result.getCodeDisplay()));
retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract()));
retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract()));
return retVal;
} finally {
endRequest(theServletRequest);
}
}
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters= {
@OperationParam(name="result", type=BooleanDt.class, min=1),
@OperationParam(name="message", type=StringDt.class),
@OperationParam(name="display", type=StringDt.class)
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanDt.class, min = 1),
@OperationParam(name = "message", type = StringDt.class),
@OperationParam(name = "display", type = StringDt.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional=true) IdDt theId,
@OperationParam(name="identifier", min=0, max=1) UriDt theValueSetIdentifier,
@OperationParam(name="code", min=0, max=1) CodeDt theCode,
@OperationParam(name="system", min=0, max=1) UriDt theSystem,
@OperationParam(name="display", min=0, max=1) StringDt theDisplay,
@OperationParam(name="coding", min=0, max=1) CodingDt theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept,
RequestDetails theRequestDetails
) {
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdDt theId,
@OperationParam(name = "identifier", min = 0, max = 1) UriDt theValueSetIdentifier,
@OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringDt theDisplay,
@OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConceptDt theCodeableConcept,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
@ -169,5 +154,19 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -1,142 +1,19 @@
package ca.uhn.fhir.jpa.provider;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.model.util.JpaConstants;
/**
* @deprecated TerminologyUploaderProvider
*/
@Deprecated
public class BaseTerminologyUploaderProvider {
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.r4.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.*;
public abstract class BaseTerminologyUploaderProvider extends BaseJpaProvider {
public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTerminologyUploaderProvider.class);
// FIXME: remove these before 4.0.0
public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM;
public static final String CONCEPT_COUNT = "conceptCount";
public static final String TARGET = "target";
@Autowired
private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc;
protected Parameters handleUploadExternalCodeSystem(
HttpServletRequest theServletRequest,
StringParam theCodeSystemUrl,
List<StringType> theLocalFile,
List<Attachment> thePackage, RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
if (theLocalFile == null || theLocalFile.size() == 0) {
if (thePackage == null || thePackage.size() == 0) {
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
}
}
try {
List<IHapiTerminologyLoaderSvc.FileDescriptor> localFiles = new ArrayList<>();
if (theLocalFile != null && theLocalFile.size() > 0) {
for (StringType nextLocalFile : theLocalFile) {
if (isNotBlank(nextLocalFile.getValue())) {
ourLog.info("Reading in local file: {}", nextLocalFile.getValue());
File nextFile = new File(nextLocalFile.getValue());
if (!nextFile.exists() || !nextFile.isFile()) {
throw new InvalidRequestException("Unknown file: " + nextFile.getName());
}
localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() {
@Override
public String getFilename() {
return nextFile.getAbsolutePath();
}
@Override
public InputStream getInputStream() {
try {
return new FileInputStream(nextFile);
} catch (FileNotFoundException theE) {
throw new InternalErrorException(theE);
}
}
});
}
}
}
if (thePackage != null) {
for (Attachment nextPackage : thePackage) {
if (isBlank(nextPackage.getUrl())) {
throw new UnprocessableEntityException("Package is missing mandatory url element");
}
localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() {
@Override
public String getFilename() {
return nextPackage.getUrl();
}
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(nextPackage.getData());
}
});
}
}
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
url = defaultString(url);
UploadStatistics stats;
switch(url) {
case IHapiTerminologyLoaderSvc.SCT_URI:
stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails);
break;
case IHapiTerminologyLoaderSvc.LOINC_URI:
stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails);
break;
case IHapiTerminologyLoaderSvc.IMGTHLA_URI:
stats = myTerminologyLoaderSvc.loadImgthla(localFiles, theRequestDetails);
break;
default:
throw new InvalidRequestException("Unknown URL: " + url);
}
Parameters retVal = new Parameters();
retVal.addParameter().setName(CONCEPT_COUNT).setValue(new IntegerType(stats.getConceptCount()));
retVal.addParameter().setName(TARGET).setValue(new Reference(stats.getTarget().getValue()));
return retVal;
} finally {
endRequest(theServletRequest);
}
}
public static final String SYSTEM = "system";
public static final String PARENT_CODE = "parentCode";
public static final String VALUE = "value";
}

View File

@ -0,0 +1,261 @@
package ca.uhn.fhir.jpa.provider;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.AttachmentUtil;
import ca.uhn.fhir.util.ParametersUtil;
import ca.uhn.fhir.util.ValidateUtil;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.commons.lang3.StringUtils.*;
public abstract class TerminologyUploaderProvider extends BaseJpaProvider {
public static final String CONCEPT_COUNT = "conceptCount";
public static final String TARGET = "target";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProvider.class);
public static final String PARENT_CODE = "parentCode";
public static final String VALUE = "value";
@Autowired
private FhirContext myCtx;
@Autowired
private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc;
@Autowired
private IHapiTerminologySvc myTerminologySvc;
/**
* <code>
* $apply-codesystem-delta-add
* </code>
*/
@Operation(typeName="CodeSystem", name = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD, idempotent = false, returnParameters = {
})
public IBaseParameters applyCodeSystemDeltaAdd(
HttpServletRequest theServletRequest,
@OperationParam(name = PARENT_CODE, min = 0, max = 1) IPrimitiveType<String> theParentCode,
@OperationParam(name = VALUE, min = 1, max = 1) IBaseResource theValue,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
CodeSystem value;
if (theValue instanceof CodeSystem) {
value = (CodeSystem) theValue;
} else if (theValue instanceof org.hl7.fhir.dstu3.model.CodeSystem) {
value = VersionConvertor_30_40.convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theValue);
} else {
throw new InvalidRequestException("Value must be present and be a CodeSystem");
}
String system = value.getUrl();
String parentCode = theParentCode != null ? theParentCode.getValue() : null;
AtomicInteger counter = myTerminologySvc.applyDeltaCodesystemsAdd(system, parentCode, value);
IBaseParameters retVal = ParametersUtil.newInstance(myCtx);
ParametersUtil.addParameterToParametersBoolean(myCtx, retVal, "success", true);
ParametersUtil.addParameterToParametersInteger(myCtx, retVal, "addedConcepts", counter.get());
return retVal;
} finally {
endRequest(theServletRequest);
}
}
/**
* <code>
* $apply-codesystem-delta-remove
* </code>
*/
@Operation(typeName="CodeSystem", name = JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE, idempotent = false, returnParameters = {
})
public IBaseParameters applyCodeSystemDeltaRemove(
HttpServletRequest theServletRequest,
@OperationParam(name = VALUE, min = 1, max = 1) IBaseResource theValue,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
CodeSystem value;
if (theValue instanceof CodeSystem) {
value = (CodeSystem) theValue;
} else if (theValue instanceof org.hl7.fhir.dstu3.model.CodeSystem) {
value = VersionConvertor_30_40.convertCodeSystem((org.hl7.fhir.dstu3.model.CodeSystem) theValue);
} else {
throw new InvalidRequestException("Value must be present and be a CodeSystem");
}
String system = value.getUrl();
AtomicInteger counter = myTerminologySvc.applyDeltaCodesystemsRemove(system, value);
IBaseParameters retVal = ParametersUtil.newInstance(myCtx);
ParametersUtil.addParameterToParametersBoolean(myCtx, retVal, "success", true);
ParametersUtil.addParameterToParametersInteger(myCtx, retVal, "removedConcepts", counter.get());
return retVal;
} finally {
endRequest(theServletRequest);
}
}
/**
* <code>
* $upload-external-codesystem
* </code>
*/
@Operation(typeName="CodeSystem", name = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = {
// @OperationParam(name = "conceptCount", type = IntegerType.class, min = 1)
})
public IBaseParameters uploadExternalCodeSystem(
HttpServletRequest theServletRequest,
@OperationParam(name = "url", min = 1, typeName = "uri") IPrimitiveType<String> theCodeSystemUrl,
@OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theLocalFile,
@OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List<ICompositeType> thePackage,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
if (theLocalFile == null || theLocalFile.size() == 0) {
if (thePackage == null || thePackage.size() == 0) {
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
}
for (ICompositeType next : thePackage) {
ValidateUtil.isTrueOrThrowInvalidRequest(myCtx.getElementDefinition(next.getClass()).getName().equals("Attachment"), "Package must be of type Attachment");
}
}
try {
List<IHapiTerminologyLoaderSvc.FileDescriptor> localFiles = new ArrayList<>();
if (theLocalFile != null && theLocalFile.size() > 0) {
for (IPrimitiveType<String> nextLocalFile : theLocalFile) {
if (isNotBlank(nextLocalFile.getValue())) {
ourLog.info("Reading in local file: {}", nextLocalFile.getValue());
File nextFile = new File(nextLocalFile.getValue());
if (!nextFile.exists() || !nextFile.isFile()) {
throw new InvalidRequestException("Unknown file: " + nextFile.getName());
}
localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() {
@Override
public String getFilename() {
return nextFile.getAbsolutePath();
}
@Override
public InputStream getInputStream() {
try {
return new FileInputStream(nextFile);
} catch (FileNotFoundException theE) {
throw new InternalErrorException(theE);
}
}
});
}
}
}
if (thePackage != null) {
for (ICompositeType nextPackage : thePackage) {
final String url = AttachmentUtil.getOrCreateUrl(myCtx, nextPackage).getValueAsString();
if (isBlank(url)) {
throw new UnprocessableEntityException("Package is missing mandatory url element");
}
localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() {
@Override
public String getFilename() {
return url;
}
@Override
public InputStream getInputStream() {
byte[] data = AttachmentUtil.getOrCreateData(myCtx, nextPackage).getValue();
return new ByteArrayInputStream(data);
}
});
}
}
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
url = defaultString(url);
UploadStatistics stats;
switch (url) {
case IHapiTerminologyLoaderSvc.SCT_URI:
stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails);
break;
case IHapiTerminologyLoaderSvc.LOINC_URI:
stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails);
break;
case IHapiTerminologyLoaderSvc.IMGTHLA_URI:
stats = myTerminologyLoaderSvc.loadImgthla(localFiles, theRequestDetails);
break;
default:
throw new InvalidRequestException("Unknown URL: " + url);
}
IBaseParameters retVal = ParametersUtil.newInstance(myCtx);
ParametersUtil.addParameterToParametersBoolean(myCtx, retVal, "success", true);
ParametersUtil.addParameterToParametersInteger(myCtx, retVal, CONCEPT_COUNT, stats.getConceptCount());
ParametersUtil.addParameterToParametersReference(myCtx, retVal, TARGET, stats.getTarget().getValue());
return retVal;
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -20,14 +20,13 @@ package ca.uhn.fhir.jpa.provider.dstu3;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
@ -36,29 +35,27 @@ import java.util.List;
public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderDstu3<CodeSystem> {
@SuppressWarnings("unchecked")
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringType.class, min=1),
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class, min = 0),
@OperationParam(name = "display", type = StringType.class, min = 1),
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeType theCode,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
RequestDetails theRequestDetails
) {
HttpServletRequest theServletRequest,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
result.throwNotFoundIfAppropriate();
org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(theProperties);
return VersionConvertor_30_40.convertParameters(parametersR4);
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
} catch (FHIRException e) {
throw new InternalErrorException(e);
} finally {
@ -70,16 +67,16 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD
/**
* $subsumes operation
*/
@Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters= {
@OperationParam(name="outcome", type= CodeType.class, min=1),
@Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters = {
@OperationParam(name = "outcome", type = CodeType.class, min = 1),
})
public Parameters subsumes(
HttpServletRequest theServletRequest,
@OperationParam(name="codeA", min=0, max=1) CodeType theCodeA,
@OperationParam(name="codeB", min=0, max=1) CodeType theCodeB,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="codingA", min=0, max=1) Coding theCodingA,
@OperationParam(name="codingB", min=0, max=1) Coding theCodingB,
@OperationParam(name = "codeA", min = 0, max = 1) CodeType theCodeA,
@OperationParam(name = "codeB", min = 0, max = 1) CodeType theCodeB,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA,
@OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB,
RequestDetails theRequestDetails
) {

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.dstu3;
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -37,37 +38,10 @@ import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
public class TerminologyUploaderProviderDstu3 extends BaseTerminologyUploaderProvider {
@Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = {
@OperationParam(name = "conceptCount", type = IntegerType.class, min = 1)
})
public Parameters uploadExternalCodeSystem(
HttpServletRequest theServletRequest,
@OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl,
@OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List<StringType> theLocalFile,
@OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List<Attachment> thePackage,
RequestDetails theRequestDetails
) {
try {
List<org.hl7.fhir.r4.model.StringType> localFile = null;
if (theLocalFile != null) {
localFile = new ArrayList<>();
for (StringType next : theLocalFile) {
localFile.add(VersionConvertor_30_40.convertString(next));
}
}
List<org.hl7.fhir.r4.model.Attachment> pkg = null;
if (thePackage!=null){
pkg = new ArrayList<>();
for (Attachment next : thePackage) {
pkg.add(VersionConvertor_30_40.convertAttachment(next));
}
}
org.hl7.fhir.r4.model.Parameters retValR4 = handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, localFile, pkg, theRequestDetails);
return VersionConvertor_30_40.convertParameters(retValR4);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
/**
* @deprecated Use {@link TerminologyUploaderProvider} instead
*/
@Deprecated
public class TerminologyUploaderProviderDstu3 extends TerminologyUploaderProvider {
// nothing
}

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4;
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
@ -55,9 +55,9 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
result.throwNotFoundIfAppropriate();
return result.toParameters(theProperties);
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
} finally {
endRequest(theServletRequest);
}

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4;
* #L%
*/
import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -33,18 +34,9 @@ import org.hl7.fhir.r4.model.StringType;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
public class TerminologyUploaderProviderR4 extends BaseTerminologyUploaderProvider {
@Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = {
@OperationParam(name = "conceptCount", type = IntegerType.class, min = 1)
})
public Parameters uploadExternalCodeSystem(
HttpServletRequest theServletRequest,
@OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl,
@OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List<StringType> theLocalFile,
@OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List<Attachment> thePackage,
RequestDetails theRequestDetails
) {
return handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, theLocalFile, thePackage, theRequestDetails);
}
/**
* @deprecated Use {@link TerminologyUploaderProvider} instead
*/
public class TerminologyUploaderProviderR4 extends TerminologyUploaderProvider {
// nothing
}

View File

@ -21,10 +21,8 @@ package ca.uhn.fhir.jpa.term;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
@ -78,6 +76,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
@ -261,6 +260,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource);
protected void validateCodeSystemForStorage(CodeSystem theCodeSystemResource) {
ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theCodeSystemResource.getUrl(), "Can not store a CodeSystem without a valid URL");
}
protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap);
abstract void createOrUpdateValueSet(ValueSet theValueSet);
@ -767,9 +770,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Override
public Optional<TermConcept> findCode(String theCodeSystem, String theCode) {
TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem);
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
/*
* Loading concepts without a transaction causes issues later on some
* platforms (e.g. PSQL) so this transactiontemplate is here to make
* sure that we always call this with an open transaction
*/
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY);
return txTemplate.execute(t->{
TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem);
return myConceptDao.findByCodeSystemAndCode(csv, theCode);
});
}
@Override
@ -1036,6 +1047,37 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
/**
* Returns the number of saved concepts
*/
private int saveOrUpdateConcept(TermConcept theConcept) {
TermCodeSystemVersion csv = theConcept.getCodeSystemVersion();
Optional<TermConcept> existing = myConceptDao.findByCodeSystemAndCode(csv, theConcept.getCode());
if (existing.isPresent()) {
TermConcept existingConcept = existing.get();
boolean haveChanges = false;
if (!StringUtils.equals(existingConcept.getDisplay(), theConcept.getDisplay())) {
existingConcept.setDisplay(theConcept.getDisplay());
haveChanges = true;
}
if (!haveChanges) {
return 0;
}
myConceptDao.save(existingConcept);
return 1;
} else {
return saveConcept(theConcept);
}
}
/**
* Returns the number of saved concepts
*/
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
@ -1124,7 +1166,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSystemVersion) {
public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion) {
ourLog.info("Storing code system");
ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied");
@ -1170,6 +1212,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
theCodeSystemVersion.setCodeSystem(codeSystem);
theCodeSystemVersion.setCodeSystemDisplayName(theSystemName);
theCodeSystemVersion.setCodeSystemVersionId(theSystemVersionId);
ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
// Validate the code system
@ -1226,8 +1271,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
ourLog.info("CodeSystem resource has ID: {}", csId.getValue());
theCodeSystemVersion.setResource(resource);
storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion);
populateCodeSystemVersionProperties(theCodeSystemVersion, theCodeSystemResource, resource);
storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemResource.getVersion(), theCodeSystemVersion);
myDeferredConceptMaps.addAll(theConceptMaps);
myDeferredValueSets.addAll(theValueSets);
@ -1235,6 +1281,99 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return csId;
}
private void populateCodeSystemVersionProperties(TermCodeSystemVersion theCodeSystemVersion, CodeSystem theCodeSystemResource, ResourceTable theResourceTable) {
theCodeSystemVersion.setResource(theResourceTable);
theCodeSystemVersion.setCodeSystemDisplayName(theCodeSystemResource.getName());
theCodeSystemVersion.setCodeSystemVersionId(theCodeSystemResource.getVersion());
}
@Override
public void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity) {
if (theCodeSystem != null && isNotBlank(theCodeSystem.getUrl())) {
String codeSystemUrl = theCodeSystem.getUrl();
if (theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.COMPLETE || theCodeSystem.getContent() == null || theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) {
ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", theResourceEntity.getIdDt().getValue(), theCodeSystem.getContentElement().getValueAsString());
Long codeSystemResourcePid = IDao.RESOURCE_PID.get(theCodeSystem);
TermCodeSystemVersion persCs = myCodeSystemVersionDao.findCurrentVersionForCodeSystemResourcePid(codeSystemResourcePid);
if (persCs != null) {
ourLog.info("Code system version already exists in database");
} else {
persCs = new TermCodeSystemVersion();
populateCodeSystemVersionProperties(persCs, theCodeSystem, theResourceEntity);
persCs.getConcepts().addAll(toPersistedConcepts(theCodeSystem.getConcept(), persCs));
ourLog.info("Code system has {} concepts", persCs.getConcepts().size());
storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, theCodeSystem.getName(), theCodeSystem.getVersion(), persCs);
}
}
}
}
private List<TermConcept> toPersistedConcepts(List<CodeSystem.ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) {
ArrayList<TermConcept> retVal = new ArrayList<>();
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
if (isNotBlank(next.getCode())) {
TermConcept termConcept = toTermConcept(next, theCodeSystemVersion);
retVal.add(termConcept);
}
}
return retVal;
}
@Nonnull
private TermConcept toTermConcept(CodeSystem.ConceptDefinitionComponent theConceptDefinition, TermCodeSystemVersion theCodeSystemVersion) {
TermConcept termConcept = new TermConcept();
termConcept.setCode(theConceptDefinition.getCode());
termConcept.setCodeSystemVersion(theCodeSystemVersion);
termConcept.setDisplay(theConceptDefinition.getDisplay());
termConcept.addChildren(toPersistedConcepts(theConceptDefinition.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA);
for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : theConceptDefinition.getDesignation()) {
if (isNotBlank(designationComponent.getValue())) {
TermConceptDesignation designation = termConcept.addDesignation();
designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null);
if (designationComponent.hasUse()) {
designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null);
designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null);
designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null);
}
designation.setValue(designationComponent.getValue());
}
}
for (CodeSystem.ConceptPropertyComponent next : theConceptDefinition.getProperty()) {
TermConceptProperty property = new TermConceptProperty();
property.setKey(next.getCode());
property.setConcept(termConcept);
property.setCodeSystemVersion(theCodeSystemVersion);
if (next.getValue() instanceof StringType) {
property.setType(TermConceptPropertyTypeEnum.STRING);
property.setValue(next.getValueStringType().getValue());
} else if (next.getValue() instanceof Coding) {
Coding nextCoding = next.getValueCoding();
property.setType(TermConceptPropertyTypeEnum.CODING);
property.setCodeSystem(nextCoding.getSystem());
property.setValue(nextCoding.getCode());
property.setDisplay(nextCoding.getDisplay());
} else if (next.getValue() != null) {
// TODO: LOINC has properties of type BOOLEAN that we should handle
ourLog.warn("Don't know how to handle properties of type: " + next.getValue().getClass());
continue;
}
termConcept.getProperties().add(property);
}
return termConcept;
}
@Override
@Transactional
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
@ -1443,6 +1582,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
@Override
@Transactional
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) {
VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA);
VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB);
@ -1471,6 +1611,136 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes);
}
@Transactional
@Override
public AtomicInteger applyDeltaCodesystemsAdd(String theSystem, @Nullable String theParent, CodeSystem theValue) {
TermCodeSystem cs = getCodeSystem(theSystem);
if (cs == null) {
List<CodeSystem.ConceptDefinitionComponent> codes = theValue.getConcept();
theValue.setConcept(null);
createOrUpdateCodeSystem(theValue);
cs = getCodeSystem(theSystem);
theValue.setConcept(codes);
}
TermCodeSystemVersion csv = cs.getCurrentVersion();
AtomicInteger addedCodeCounter = new AtomicInteger(0);
TermConcept parentCode = null;
if (isNotBlank(theParent)) {
parentCode = myConceptDao
.findByCodeSystemAndCode(csv, theParent)
.orElseThrow(() -> new InvalidRequestException("Unknown code [" + theSystem + "|" + theParent + "]"));
}
List<TermConcept> concepts = new ArrayList<>();
for (CodeSystem.ConceptDefinitionComponent next : theValue.getConcept()) {
TermConcept concept = toTermConcept(next, csv);
if (parentCode != null) {
parentCode.addChild(concept, RelationshipTypeEnum.ISA);
}
concepts.add(concept);
}
// The first pass just saves any concepts that were added to the
// root of the CodeSystem
List<TermConceptParentChildLink> links = new ArrayList<>();
for (TermConcept next : concepts) {
int addedCount = saveOrUpdateConcept(next);
addedCodeCounter.addAndGet(addedCount);
extractLinksFromConceptAndChildren(next, links);
}
// This second pass saves any child concepts
for (TermConceptParentChildLink next : links) {
next.setCodeSystem(csv);
int addedCount = saveOrUpdateConcept(next.getChild());
addedCodeCounter.addAndGet(addedCount);
myConceptParentChildLinkDao.save(next);
}
return addedCodeCounter;
}
@Transactional
@Override
public AtomicInteger applyDeltaCodesystemsRemove(String theSystem, CodeSystem theValue) {
TermCodeSystem cs = getCodeSystem(theSystem);
if (cs == null) {
throw new InvalidRequestException("Unknown code system: " + theSystem);
}
AtomicInteger removeCounter = new AtomicInteger(0);
for (CodeSystem.ConceptDefinitionComponent next : theValue.getConcept()) {
Optional<TermConcept> conceptOpt = findCode(theSystem, next.getCode());
if (conceptOpt.isPresent()) {
TermConcept concept = conceptOpt.get();
deleteConceptChildrenAndConcept(concept, removeCounter);
}
}
return removeCounter;
}
private void deleteConceptChildrenAndConcept(TermConcept theConcept, AtomicInteger theRemoveCounter) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
deleteConceptChildrenAndConcept(nextChildLink.getChild(), theRemoveCounter);
myConceptParentChildLinkDao.delete(nextChildLink);
}
myConceptDesignationDao.deleteAll(theConcept.getDesignations());
myConceptPropertyDao.deleteAll(theConcept.getProperties());
myConceptDao.delete(theConcept);
theRemoveCounter.incrementAndGet();
}
protected IContextValidationSupport.LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
return txTemplate.execute(t -> {
Optional<TermConcept> codeOpt = findCode(theSystem, theCode);
if (codeOpt.isPresent()) {
TermConcept code = codeOpt.get();
IContextValidationSupport.LookupCodeResult result = new IContextValidationSupport.LookupCodeResult();
result.setCodeSystemDisplayName(code.getCodeSystemVersion().getCodeSystemDisplayName());
result.setCodeSystemVersion(code.getCodeSystemVersion().getCodeSystemVersionId());
result.setSearchedForSystem(theSystem);
result.setSearchedForCode(theCode);
result.setFound(true);
result.setCodeDisplay(code.getDisplay());
for (TermConceptDesignation next : code.getDesignations()) {
IContextValidationSupport.ConceptDesignation designation = new IContextValidationSupport.ConceptDesignation();
designation.setLanguage(next.getLanguage());
designation.setUseSystem(next.getUseSystem());
designation.setUseCode(next.getUseCode());
designation.setUseDisplay(next.getUseDisplay());
designation.setValue(next.getValue());
result.getDesignations().add(designation);
}
for (TermConceptProperty next : code.getProperties()) {
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
IContextValidationSupport.CodingConceptProperty property = new IContextValidationSupport.CodingConceptProperty(next.getKey(), next.getCodeSystem(), next.getValue(), next.getDisplay());
result.getProperties().add(property);
} else if (next.getType() == TermConceptPropertyTypeEnum.STRING) {
IContextValidationSupport.StringConceptProperty property = new IContextValidationSupport.StringConceptProperty(next.getKey(), next.getValue());
result.getProperties().add(property);
} else {
throw new InternalErrorException("Unknown type: " + next.getType());
}
}
return result;
} else {
return null;
}
});
}
private @Nullable
ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) {
QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
@ -1608,7 +1878,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
predicates = new ArrayList<>();
coding = translationQuery.getCoding();
String targetCode = null;
String targetCode;
String targetCodeSystem = null;
if (coding.hasCode()) {
predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode()));
@ -1716,12 +1986,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return retVal;
}
private void verifyNoDuplicates(Collection<TermConcept> theConcepts, Set<String> theCodes) {
for (TermConcept next : theConcepts) {
if (!theCodes.add(next.getCode())) {
throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes");
}
verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes);
private static void extractLinksFromConceptAndChildren(TermConcept theConcept, List<TermConceptParentChildLink> theLinks) {
theLinks.addAll(theConcept.getParents());
for (TermConceptParentChildLink child : theConcept.getChildren()) {
extractLinksFromConceptAndChildren(child.getChild(), theLinks);
}
}
@ -1775,4 +2043,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) {
ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest;
}
}

View File

@ -19,6 +19,9 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.ArrayList;
import java.util.Collections;
@ -61,6 +64,8 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
private IValidationSupport myValidationSupport;
@Autowired
private IHapiTerminologySvc myTerminologySvc;
@Autowired
private PlatformTransactionManager myTransactionManager;
/**
* Constructor
@ -100,6 +105,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
validateCodeSystemForStorage(theCodeSystemResource);
if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId();
@ -217,14 +223,14 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
return null;
}
@CoverageIgnore
@Override
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
public IBaseResource fetchResource(FhirContext theContext, Class theClass, String theUri) {
return null;
}
@CoverageIgnore
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
return null;
}
@ -293,20 +299,30 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
@CoverageIgnore
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) {
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
TermConcept code = codeOpt.get();
def.setCode(code.getCode());
def.setDisplay(code.getDisplay());
CodeValidationResult retVal = new CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties());
retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
return retVal;
}
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txTemplate.execute(t->{
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) {
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
TermConcept code = codeOpt.get();
def.setCode(code.getCode());
def.setDisplay(code.getDisplay());
IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties());
retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
return retVal;
}
return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
});
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return super.lookupCode(theContext, theSystem, theCode);
}
@Override

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.ValidateUtil;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
@ -18,6 +19,9 @@ import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.ArrayList;
import java.util.Collections;
@ -48,6 +52,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements IHapiTerminologySvcR4 {
@Autowired
@Qualifier("myConceptMapDaoR4")
private IFhirResourceDao<ConceptMap> myConceptMapResourceDao;
@ -60,7 +65,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private IHapiTerminologySvc myTerminologySvc;
private PlatformTransactionManager myTransactionManager;
private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
if (isNotBlank(theCode.getCode())) {
@ -87,6 +92,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override
protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
validateCodeSystemForStorage(theCodeSystemResource);
if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId();
@ -227,7 +233,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return myTerminologySvc.supportsSystem(theSystem);
return supportsSystem(theSystem);
}
@Override
@ -237,19 +243,28 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@CoverageIgnore
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) {
TermConcept code = codeOpt.get();
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
def.setCode(code.getCode());
def.setDisplay(code.getDisplay());
CodeValidationResult retVal = new CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties());
return retVal;
}
public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txTemplate.execute(t-> {
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) {
TermConcept code = codeOpt.get();
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
def.setCode(code.getCode());
def.setDisplay(code.getDisplay());
IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties());
return retVal;
}
return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
});
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return super.lookupCode(theContext, theSystem, theCode);
}
}

View File

@ -8,12 +8,15 @@ import org.hl7.fhir.instance.model.api.IBaseCoding;
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 org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/*
* #%L
@ -76,13 +79,15 @@ public interface IHapiTerminologySvc {
*/
void setProcessDeferred(boolean theProcessDeferred);
void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSytemVersion);
void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion);
/**
* @return Returns the ID of the created/updated code system
*/
IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List<org.hl7.fhir.r4.model.ValueSet> theValueSets, List<org.hl7.fhir.r4.model.ConceptMap> theConceptMaps);
void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity);
void deleteConceptMapAndChildren(ResourceTable theResourceTable);
void deleteValueSetAndChildren(ResourceTable theResourceTable);
@ -98,4 +103,8 @@ public interface IHapiTerminologySvc {
List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest);
IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB);
AtomicInteger applyDeltaCodesystemsAdd(String theSystem, @Nullable String theParent, CodeSystem theValue);
AtomicInteger applyDeltaCodesystemsRemove(String theSystem, CodeSystem theDelta);
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
@ -63,6 +63,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("ACME Codes");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new);
@ -98,7 +99,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA");
parentC.addChild(childCA, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs);
return codeSystem;
}
@ -129,7 +130,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
parentB.addChild(childI, RelationshipTypeEnum.ISA);
}
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs);
return codeSystem;
}
@ -165,7 +166,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME", "SYSTEM VERSION" , cs);
return codeSystem;
}
@ -489,14 +490,18 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
createExternalCsAndLocalVs();
// We're making sure that a reindex doesn't wipe out all of the
// stored codes in the terminology service
myResourceReindexingSvc.markAllResourcesForReindexing();
myResourceReindexingSvc.forceReindexingPass();
myResourceReindexingSvc.forceReindexingPass();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
IContextValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd);
assertEquals(true, lookupResults.isFound());
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
@ -680,11 +685,11 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
cs.setResource(table);
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", "SYSTEM VERSION" , cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
IContextValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}

View File

@ -22,6 +22,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
@ -127,6 +128,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired
protected ITermCodeSystemDao myTermCodeSystemDao;
@Autowired
protected ITermConceptParentChildLinkDao myTermConceptParentChildLinkDao;
@Autowired
protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao;
@Autowired
@Qualifier("myCompartmentDefinitionDaoR4")
@ -288,7 +291,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Qualifier("myTaskDaoR4")
protected IFhirResourceDao<Task> myTaskDao;
@Autowired
protected IHapiTerminologySvc myTermSvc;
protected IHapiTerminologySvcR4 myTermSvc;
@Autowired
protected PlatformTransactionManager myTransactionMgr;
@Autowired

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
@ -96,7 +96,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA");
parentC.addChild(childCA, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs);
return codeSystem;
}
@ -132,7 +132,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs);
return codeSystem;
}
@ -163,7 +163,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
parentB.addChild(childI, RelationshipTypeEnum.ISA);
}
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs);
return codeSystem;
}
@ -476,7 +476,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
concept = new TermConcept(cs, "LA9999-7");
cs.getConcepts().add(concept);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs);
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
@ -803,11 +803,11 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
cs.setResource(table);
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", "SYSTEM VERSION" , cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
IContextValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}

View File

@ -55,10 +55,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals(("SYSTEM VERSION"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
// With HTTP GET
respParam = ourClient
@ -75,10 +77,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("SYSTEM VERSION", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
}
@ -96,11 +100,13 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
assertEquals(("v2 Identifier Type"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals(("2.8.2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
}
@Test
@ -123,7 +129,6 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
@Test
public void testLookupOperationByCodeAndSystemUserDefinedCode() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
@ -131,13 +136,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
.withParameter(Parameters.class, "code", new CodeType("8450-9"))
.andParameter("system", new UriType("http://acme.org"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("ACME Codes", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -164,20 +168,18 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
@Test
public void testLookupOperationByCoding() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
.named("lookup")
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -239,9 +241,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
}
@Test
// @Ignore
public void testLookupOperationForBuiltInCode() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(CodeSystem.class)
@ -249,17 +249,18 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
.withParameter(Parameters.class, "code", new CodeType("M"))
.andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus"))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue());
assertEquals("v3 Code System MaritalStatus", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("2016-11-11", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Married", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue());
}
@Test

View File

@ -126,7 +126,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
code.addPropertyString("HELLO", "12345-2");
cs.getConcepts().add(code);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs);
});
}
@ -233,7 +233,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
@Test
public void testExpandById() throws IOException {
public void testExpandById() {
//@formatter:off
Parameters respParam = ourClient
.operation()
@ -263,7 +263,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandByIdWithFilter() throws IOException {
public void testExpandByIdWithFilter() {
//@formatter:off
Parameters respParam = ourClient
@ -350,7 +350,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandInlineVsAgainstBuiltInCs() throws IOException {
public void testExpandInlineVsAgainstBuiltInCs() {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
@ -371,7 +371,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandInlineVsAgainstExternalCs() throws IOException {
public void testExpandInlineVsAgainstExternalCs() {
createExternalCsAndLocalVs();
assertNotNull(myLocalVs);
myLocalVs.setId("");
@ -446,7 +446,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandLocalVsAgainstBuiltInCs() throws IOException {
public void testExpandLocalVsAgainstBuiltInCs() {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
@ -467,7 +467,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandLocalVsAgainstExternalCs() throws IOException {
public void testExpandLocalVsAgainstExternalCs() {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
@ -491,7 +491,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException {
public void testExpandLocalVsCanonicalAgainstExternalCs() {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
@ -515,7 +515,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandLocalVsWithUnknownCode() throws IOException {
public void testExpandLocalVsWithUnknownCode() {
createExternalCsAndLocalVsWithUnknownCode();
assertNotNull(myLocalValueSetId);
@ -636,7 +636,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), Matchers.containsStringIgnoringCase("succeeded"));
@ -664,7 +664,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), Matchers.containsStringIgnoringCase("succeeded"));
@ -682,6 +682,8 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("ACME Codes");
codeSystem.setVersion("1.2.3.4");
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new);
@ -707,7 +709,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs);
return codeSystem;
}

View File

@ -85,7 +85,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO"))
.andParameter("package", new Attachment().setUrl("foo").setData(packageBytes))
@ -102,7 +102,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
@ -111,7 +111,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
/*
* Try uploading a second time
@ -119,7 +119,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
@ -136,7 +136,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("package", new Attachment().setData(packageBytes))
@ -154,7 +154,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "package", new Attachment().setUrl("foo").setData(packageBytes))
.execute();
@ -170,7 +170,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.execute();
@ -186,7 +186,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
@ -195,7 +195,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
}
@Test
@ -212,7 +212,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
@ -221,7 +221,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
}
@AfterClass

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TestUtil;
@ -14,8 +16,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.*;
public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test {
@ -45,6 +47,93 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
}
@Test
public void testApplyDeltaAdd() {
CodeSystem delta = new CodeSystem();
delta.setUrl("http://example.com/labCodes");
delta.setName("Example Hospital Lab Codes");
delta.setStatus(Enumerations.PublicationStatus.ACTIVE);
delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
delta.setUrl("http://foo");
CodeSystem.ConceptDefinitionComponent chem = delta
.addConcept()
.setCode("CHEM")
.setDisplay("Chemistry Tests");
chem
.addConcept()
.setCode("HB")
.setDisplay("Hemoglobin");
chem
.addConcept()
.setCode("NEUT")
.setDisplay("Neutrophil");
CodeSystem.ConceptDefinitionComponent micro = delta
.addConcept()
.setCode("MICRO")
.setDisplay("Microbiology Tests");
micro
.addConcept()
.setCode("C&S")
.setDisplay("Culture & Sensitivity");
LoggingInterceptor interceptor = new LoggingInterceptor(true);
ourClient.registerInterceptor(interceptor);
Parameters outcome = ourClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD)
.withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta)
.prettyPrint()
.execute();
ourClient.unregisterInterceptor(interceptor);
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(encoded);
assertThat(encoded, containsString("\"valueInteger\": 5"));
}
@Test
public void testApplyDeltaRemove() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
ourClient.create().resource(cs).execute();
CodeSystem delta = new CodeSystem();
delta.setUrl("http://foo");
delta
.addConcept()
.setCode("codeA")
.setDisplay("displayA");
// Add
ourClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD)
.withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta)
.prettyPrint()
.execute();
// Remove
LoggingInterceptor interceptor = new LoggingInterceptor(true);
ourClient.registerInterceptor(interceptor);
Parameters outcome = ourClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE)
.withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta)
.prettyPrint()
.execute();
ourClient.unregisterInterceptor(interceptor);
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(encoded);
assertThat(encoded, containsString("\"valueInteger\": 1"));
}
@Test
public void testLookupOnExternalCode() {
ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd);
@ -61,11 +150,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("SYSTEM VERSION", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
// With HTTP GET
respParam = ourClient
@ -81,11 +172,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals(("SYSTEM VERSION"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
}
@ -103,11 +196,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue());
}
@Test
@ -140,7 +235,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -176,7 +271,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -245,11 +340,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue());
assertEquals("v3.MaritalStatus", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("2018-08-12", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Married", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(3).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue());
}
@Test

View File

@ -16,7 +16,6 @@ import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IIdType;
@ -514,7 +513,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
@ -603,7 +602,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs);
return codeSystem;
}

View File

@ -49,7 +49,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO"))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
@ -65,35 +65,31 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
public void testUploadLoinc() throws Exception {
byte[] packageBytes = TerminologyUploaderProviderDstu3Test.createLoincZip();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((Reference) respParam.getParameter().get(1).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9]+"));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
assertThat(((Reference) respParam.getParameter().get(2).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9]+"));
/*
* Try uploading a second time
*/
//@formatter:off
respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
.execute();
//@formatter:on
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
@ -106,7 +102,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.execute();
@ -114,18 +110,16 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadMissingUrl() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
try {
ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "package", new Attachment().setUrl("file.zip").setData(packageBytes))
.execute();
@ -133,27 +127,25 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadSct() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
}
@Test
@ -166,20 +158,18 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
fos.write(packageBytes);
fos.close();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.onType(CodeSystem.class)
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
}
@AfterClass

View File

@ -52,6 +52,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
@Autowired
private SubscriptionTestUtil mySubscriptionTestUtil;
@Override
@After
public void after() throws Exception {
super.after();
@ -65,6 +66,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
myWebSocketClient.stop();
}
@Override
@Before
public void before() throws Exception {
super.before();
@ -117,7 +119,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
}
@Test
public void createObservation() throws Exception {
public void createObservation() {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
@ -141,7 +143,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
}
@Test
public void createObservationThatDoesNotMatch() throws Exception {
public void createObservationThatDoesNotMatch() {
Observation observation = new Observation();
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);

View File

@ -1,38 +1,22 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@RunWith(MockitoJUnitRunner.class)
public class TerminologyLoaderSvcImgthlaTest {

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
import ca.uhn.fhir.util.TestUtil;
@ -134,9 +134,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files);
myLoader.loadLoinc(files.getFiles(), mySrd);
IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(null);
Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4);
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
Parameters parameters = (Parameters) result.toParameters(myFhirCtx, null);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters));
@ -165,9 +164,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files);
myLoader.loadLoinc(files.getFiles(), mySrd);
IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("17788-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(null);
Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4);
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("17788-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
Parameters parameters = (Parameters) result.toParameters(myFhirCtx, null);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters));
@ -184,10 +182,9 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files);
myLoader.loadLoinc(files.getFiles(), mySrd);
IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd);
List<? extends IPrimitiveType<String>> properties = Lists.newArrayList(new CodeType("SCALE_TYP"));
org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(properties);
Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4);
Parameters parameters = (Parameters) result.toParameters(myFhirCtx, properties);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters));

View File

@ -52,6 +52,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("SYSTEM NAME");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
@ -95,7 +96,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs);
return id;
}
@ -114,7 +115,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermConcept parentA = new TermConcept(cs, "CS2");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", "SYSTEM VERSION" , cs);
return id;
}
@ -146,7 +147,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
code.addPropertyString("HELLO", "12345-2");
cs.getConcepts().add(code);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs);
});
}
@ -162,7 +163,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs);
// Update
cs = new TermCodeSystemVersion();
@ -171,17 +172,14 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified();
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
cs.setResource(table);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs);
// Try to update to a different resource
codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
cs.setResource(table);
try {
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/"));
@ -585,7 +583,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
child.addChild(parent, RelationshipTypeEnum.ISA);
try {
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION" , cs);
fail();
} catch (InvalidRequestException e) {
assertEquals("CodeSystem contains circular reference around code parent", e.getMessage());
@ -708,7 +706,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
runInTransaction(()->{
ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong());
version.setResource(resTable);
myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", version);
myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", "SYSTEM VERSION" , version);
});
org.hl7.fhir.dstu3.model.ValueSet vs = new org.hl7.fhir.dstu3.model.ValueSet();

View File

@ -1,14 +1,22 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import org.junit.*;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
@ -18,9 +26,12 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
@ -31,13 +42,12 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class);
@Rule
public final ExpectedException expectedException = ExpectedException.none();
@Mock
IValueSetCodeAccumulator myValueSetCodeAccumulator;
private IIdType myConceptMapId;
private IIdType myExtensionalCsId;
private IIdType myExtensionalVsId;
@Mock
IValueSetCodeAccumulator myValueSetCodeAccumulator;
@Before
public void before() {
myDaoConfig.setAllowExternalReferences(true);
@ -53,6 +63,8 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("SYSTEM NAME");
codeSystem.setVersion("SYSTEM VERSION");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
@ -95,11 +107,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs);
return id;
}
private void createAndPersistConceptMap() {
ConceptMap conceptMap = createConceptMap();
persistConceptMap(conceptMap);
@ -157,6 +169,304 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
});
}
@Test
public void testApplyCodeSystemDeltaAdd() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
myCodeSystemDao.create(cs);
CodeSystem delta = new CodeSystem();
delta
.addConcept()
.setCode("codeA")
.setDisplay("displayA");
delta
.addConcept()
.setCode("codeB")
.setDisplay("displayB");
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
assertEquals(true, myTermSvc.findCode("http://foo", "codeA").isPresent());
assertEquals(false, myTermSvc.findCode("http://foo", "codeZZZ").isPresent());
}
/**
* This would be a good check, but there is no easy eay to do it...
*/
@Test
@Ignore
public void testApplyCodeSystemDeltaAddNotPermittedForNonExternalCodeSystem() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE);
myCodeSystemDao.create(cs);
CodeSystem delta = new CodeSystem();
delta
.addConcept()
.setCode("codeA")
.setDisplay("displayA");
try {
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
fail();
} catch (InvalidRequestException e) {
assertEquals("", e.getMessage());
}
}
@Test
public void testApplyCodeSystemDeltaAddWithoutPreExistingCodeSystem() {
CodeSystem delta = new CodeSystem();
delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
delta.setUrl("http://foo");
delta.setName("Acme Lab Codes");
delta
.addConcept()
.setCode("CBC")
.setDisplay("Complete Blood Count");
delta
.addConcept()
.setCode("URNL")
.setDisplay("Routine Urinalysis");
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
params.add(CodeSystem.SP_URL, new UriParam("http://foo"));
IBundleProvider searchResult = myCodeSystemDao.search(params, mySrd);
assertEquals(1, searchResult.size().intValue());
CodeSystem outcome = (CodeSystem) searchResult.getResources(0,1).get(0);
assertEquals("http://foo", outcome.getUrl());
assertEquals("Acme Lab Codes", outcome.getName());
}
@Test
public void testApplyCodeSystemDeltaAddDuplicatesIgnored() {
// Add codes
CodeSystem delta = new CodeSystem();
delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
delta.setUrl("http://foo");
delta.setName("Acme Lab Codes");
delta
.addConcept()
.setCode("codea")
.setDisplay("CODEA0");
delta
.addConcept()
.setCode("codeb")
.setDisplay("CODEB0");
AtomicInteger outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
assertEquals(2, outcome.get());
// Add codes again with different display
delta = new CodeSystem();
delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
delta.setUrl("http://foo");
delta.setName("Acme Lab Codes");
delta
.addConcept()
.setCode("codea")
.setDisplay("CODEA1");
delta
.addConcept()
.setCode("codeb")
.setDisplay("CODEB1");
outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
assertEquals(2, outcome.get());
// Add codes again with no changes
outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
assertEquals(0, outcome.get());
}
@Test
public void testApplyCodeSystemDeltaAddAsChild() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
myCodeSystemDao.create(cs);
CodeSystem delta = new CodeSystem();
delta
.addConcept()
.setCode("codeA")
.setDisplay("displayA");
delta
.addConcept()
.setCode("codeB")
.setDisplay("displayB");
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
delta = new CodeSystem();
CodeSystem.ConceptDefinitionComponent codeAA = delta
.addConcept()
.setCode("codeAA")
.setDisplay("displayAA");
codeAA
.addConcept()
.setCode("codeAAA")
.setDisplay("displayAAA");
myTermSvc.applyDeltaCodesystemsAdd("http://foo", "codeA", delta);
assertEquals(true, myTermSvc.findCode("http://foo", "codeAA").isPresent());
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeA"), toString("codeAA"), toString("http://foo"), null, null).getOutcome());
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeA"), toString("codeAAA"), toString("http://foo"), null, null).getOutcome());
assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeAA"), toString("codeAAA"), toString("http://foo"), null, null).getOutcome());
assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED, myTermSvc.subsumes(toString("codeB"), toString("codeAA"), toString("http://foo"), null, null).getOutcome());
runInTransaction(() -> {
List<TermConceptParentChildLink> allChildren = myTermConceptParentChildLinkDao.findAll();
assertEquals(2, allChildren.size());
});
}
@Test
public void testApplyCodeSystemDeltaAddWithPropertiesAndDesignations() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setName("Description of my life");
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
cs.setVersion("1.2.3");
myCodeSystemDao.create(cs);
CodeSystem delta = new CodeSystem();
CodeSystem.ConceptDefinitionComponent concept = delta
.addConcept()
.setCode("lunch")
.setDisplay("I'm having dog food");
concept
.addDesignation()
.setLanguage("fr")
.setUse(new Coding("http://sys", "code", "display"))
.setValue("Je mange une pomme");
concept
.addDesignation()
.setLanguage("es")
.setUse(new Coding("http://sys", "code", "display"))
.setValue("Como una pera");
concept.addProperty()
.setCode("flavour")
.setValue(new StringType("Hints of lime"));
concept.addProperty()
.setCode("useless_sct_code")
.setValue(new Coding("http://snomed.info", "1234567", "Choked on large meal (finding)"));
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
IContextValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(myFhirCtx, "http://foo", "lunch");
assertEquals(true, result.isFound());
assertEquals("lunch", result.getSearchedForCode());
assertEquals("http://foo", result.getSearchedForSystem());
Parameters output = (Parameters) result.toParameters(myFhirCtx, null);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals("Description of my life", ((StringType) output.getParameter("name")).getValue());
assertEquals("1.2.3", ((StringType) output.getParameter("version")).getValue());
assertEquals(false, output.getParameterBool("abstract"));
List<Parameters.ParametersParameterComponent> designations = output.getParameter().stream().filter(t -> t.getName().equals("designation")).collect(Collectors.toList());
assertEquals("language", designations.get(0).getPart().get(0).getName());
assertEquals("fr", ((CodeType) designations.get(0).getPart().get(0).getValue()).getValueAsString());
assertEquals("use", designations.get(0).getPart().get(1).getName());
assertEquals("http://sys", ((Coding) designations.get(0).getPart().get(1).getValue()).getSystem());
assertEquals("code", ((Coding) designations.get(0).getPart().get(1).getValue()).getCode());
assertEquals("display", ((Coding) designations.get(0).getPart().get(1).getValue()).getDisplay());
assertEquals("value", designations.get(0).getPart().get(2).getName());
assertEquals("Je mange une pomme", ((StringType) designations.get(0).getPart().get(2).getValue()).getValueAsString());
List<Parameters.ParametersParameterComponent> properties = output.getParameter().stream().filter(t -> t.getName().equals("property")).collect(Collectors.toList());
assertEquals("code", properties.get(0).getPart().get(0).getName());
assertEquals("flavour", ((CodeType) properties.get(0).getPart().get(0).getValue()).getValueAsString());
assertEquals("value", properties.get(0).getPart().get(1).getName());
assertEquals("Hints of lime", ((StringType) properties.get(0).getPart().get(1).getValue()).getValueAsString());
assertEquals("code", properties.get(1).getPart().get(0).getName());
assertEquals("useless_sct_code", ((CodeType) properties.get(1).getPart().get(0).getValue()).getValueAsString());
assertEquals("value", properties.get(1).getPart().get(1).getName());
assertEquals("http://snomed.info", ((Coding) properties.get(1).getPart().get(1).getValue()).getSystem());
assertEquals("1234567", ((Coding) properties.get(1).getPart().get(1).getValue()).getCode());
assertEquals("Choked on large meal (finding)", ((Coding) properties.get(1).getPart().get(1).getValue()).getDisplay());
}
@Test
public void testApplyCodeSystemDeltaRemove() {
// Create not-present
CodeSystem cs = new CodeSystem();
cs.setUrl("http://foo");
cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
myCodeSystemDao.create(cs);
CodeSystem delta = new CodeSystem();
CodeSystem.ConceptDefinitionComponent codeA = delta
.addConcept()
.setCode("codeA")
.setDisplay("displayA");
delta
.addConcept()
.setCode("codeB")
.setDisplay("displayB");
CodeSystem.ConceptDefinitionComponent codeAA = codeA
.addConcept()
.setCode("codeAA")
.setDisplay("displayAA");
codeAA
.addConcept()
.setCode("codeAAA")
.setDisplay("displayAAA");
myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta);
// Remove CodeB
delta = new CodeSystem();
delta
.addConcept()
.setCode("codeB")
.setDisplay("displayB");
myTermSvc.applyDeltaCodesystemsRemove("http://foo", delta);
assertEquals(false, myTermSvc.findCode("http://foo", "codeB").isPresent());
assertEquals(true, myTermSvc.findCode("http://foo", "codeA").isPresent());
assertEquals(true, myTermSvc.findCode("http://foo", "codeAA").isPresent());
assertEquals(true, myTermSvc.findCode("http://foo", "codeAAA").isPresent());
// Remove CodeA
delta = new CodeSystem();
delta
.addConcept()
.setCode("codeA");
myTermSvc.applyDeltaCodesystemsRemove("http://foo", delta);
assertEquals(false, myTermSvc.findCode("http://foo", "codeB").isPresent());
assertEquals(false, myTermSvc.findCode("http://foo", "codeA").isPresent());
assertEquals(false, myTermSvc.findCode("http://foo", "codeAA").isPresent());
assertEquals(false, myTermSvc.findCode("http://foo", "codeAAA").isPresent());
}
@Nonnull
private StringType toString(String theString) {
return new StringType(theString);
}
@Test
public void testCreateConceptMapWithMissingSourceSystem() {
ConceptMap conceptMap = new ConceptMap();
@ -183,7 +493,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
@Test
public void testCreateConceptMapWithVirtualSourceSystem() {
ConceptMap conceptMap = createConceptMap();
conceptMap.getGroup().forEach(t->t.setSource(null));
conceptMap.getGroup().forEach(t -> t.setSource(null));
conceptMap.setSource(new CanonicalType("http://hl7.org/fhir/uv/livd/StructureDefinition/loinc-livd"));
persistConceptMap(conceptMap);
@ -283,6 +593,17 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
verify(myValueSetCodeAccumulator, times(9)).includeCodeWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection());
}
@Test
public void testValidateCode() {
createCodeSystem();
IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ParentWithNoChildrenA", null);
assertEquals(true, validation.isOk());
validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ZZZZZZZ", null);
assertEquals(false, validation.isOk());
}
@Test
public void testStoreTermCodeSystemAndChildren() throws Exception {
loadAndPersistCodeSystemWithDesignations();

View File

@ -1,5 +1,6 @@
<CodeSystem xmlns="http://hl7.org/fhir">
<url value="http://acme.org" />
<name value="ACME Codes" />
<concept>
<code value="8450-9" />
<display value="Systolic blood pressure--expiration" />
@ -96,4 +97,4 @@
<code value="8492-1" />
<display value="Systolic blood pressure 8 hour minimum" />
</concept>
</CodeSystem>
</CodeSystem>

View File

@ -23,6 +23,17 @@ package ca.uhn.fhir.jpa.model.util;
import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants {
/**
* Operation name for the $apply-codesystem-delta-add operation
*/
public static final String OPERATION_APPLY_CODESYSTEM_DELTA_ADD = "$apply-codesystem-delta-add";
/**
* Operation name for the $apply-codesystem-delta-remove operation
*/
public static final String OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE = "$apply-codesystem-delta-remove";
/**
* Operation name for the $expunge operation
*/
@ -158,6 +169,11 @@ public class JpaConstants {
*/
public static final String OPERATION_BINARY_ACCESS_WRITE = "$binary-access-write";
/**
* Operation name for the "$upload-external-code-system" operation
*/
public static final String OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system";
/**
* <p>
* This extension should be of type <code>string</code> and should be

View File

@ -1,7 +1,6 @@
package ca.uhn.fhirtest.interceptor;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
@ -35,7 +34,9 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
if (isBlank(authHeader)) {
return new RuleBuilder()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andAllowAllResponses().andThen()
.deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD).atAnyLevel().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE).atAnyLevel().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().andAllowAllResponses().andThen()

View File

@ -445,15 +445,6 @@ public abstract class BaseMethodBinding<T> {
return theReturnTypeFromMethod.equals(IBaseResource.class) || theReturnTypeFromMethod.equals(IResource.class) || theReturnTypeFromMethod.equals(IAnyResource.class);
}
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
try {
String responseText = IOUtils.toString(theResponseReader);
theEx.setResponseBody(responseText);
} catch (IOException e) {
ourLog.debug("Failed to read response", e);
}
}
private static String toLogString(Class<?> theType) {
if (theType == null) {
return null;

View File

@ -68,14 +68,15 @@ public class MethodUtil {
@SuppressWarnings("unchecked")
public static List<IParameter> getResourceParameters(final FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) {
List<IParameter> parameters = new ArrayList<IParameter>();
List<IParameter> parameters = new ArrayList<>();
Class<?>[] parameterTypes = theMethod.getParameterTypes();
int paramIndex = 0;
for (Annotation[] annotations : theMethod.getParameterAnnotations()) {
IParameter param = null;
Class<?> parameterType = parameterTypes[paramIndex];
Class<?> declaredParameterType = parameterTypes[paramIndex];
Class<?> parameterType = declaredParameterType;
Class<? extends java.util.Collection<?>> outerCollectionType = null;
Class<? extends java.util.Collection<?>> innerCollectionType = null;
if (TagList.class.isAssignableFrom(parameterType)) {
@ -85,11 +86,13 @@ public class MethodUtil {
if (Collection.class.isAssignableFrom(parameterType)) {
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
declaredParameterType = parameterType;
}
if (Collection.class.isAssignableFrom(parameterType)) {
outerCollectionType = innerCollectionType;
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
declaredParameterType = parameterType;
}
if (Collection.class.isAssignableFrom(parameterType)) {
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
@ -223,7 +226,7 @@ public class MethodUtil {
param = new OperationParameter(theContext, op.name(), operationParam);
if (isNotBlank(operationParam.typeName())) {
Class<?> newParameterType = theContext.getElementDefinition(operationParam.typeName()).getImplementingClass();
if (!parameterType.isAssignableFrom(newParameterType)) {
if (!declaredParameterType.isAssignableFrom(newParameterType)) {
throw new ConfigurationException("Non assignable parameter typeName=\"" + operationParam.typeName() + "\" specified on method " + theMethod);
}
parameterType = newParameterType;

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
@ -72,7 +73,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
private boolean myManualResponseMode;
protected OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType,
boolean theIdempotent, String theOperationName, Class<? extends IBaseResource> theOperationType, String theOperationTypeName,
OperationParam[] theReturnParams, BundleTypeEnum theBundleType) {
super(theReturnResourceType, theMethod, theContext, theProvider);
@ -99,12 +100,18 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
myName = theOperationName;
if (theReturnTypeFromRp != null) {
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
} else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
} else {
setResourceName(null);
try {
if (theReturnTypeFromRp != null) {
setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName());
} else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) {
setResourceName(theContext.getResourceDefinition(theOperationType).getName());
} else if (isNotBlank(theOperationTypeName)) {
setResourceName(theContext.getResourceDefinition(theOperationTypeName).getName());
} else {
setResourceName(null);
}
} catch (DataFormatException e) {
throw new ConfigurationException("Failed to bind method " + theMethod + " - " + e.getMessage(), e);
}
if (theMethod.getReturnType().equals(IBundleProvider.class)) {
@ -160,7 +167,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
*/
public OperationMethodBinding(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
Operation theAnnotation) {
this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.returnParameters(),
this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.typeName(), theAnnotation.returnParameters(),
theAnnotation.bundleType());
myManualRequestMode = theAnnotation.manualRequest();

View File

@ -187,7 +187,7 @@ public class OperationParameter implements IParameter {
mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType);
myConverter = new OperationParamConverter();
} else {
throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName());
throw new ConfigurationException("Invalid type for @OperationParam on method " + theMethod + ": " + myParameterType.getName());
}
}

View File

@ -37,7 +37,7 @@ public class ValidateMethodBindingDstu2Plus extends OperationMethodBinding {
public ValidateMethodBindingDstu2Plus(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
Validate theAnnotation) {
super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION);
super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), null, new OperationParam[0], BundleTypeEnum.COLLECTION);
List<IParameter> newParams = new ArrayList<IParameter>();
int idx = 0;

View File

@ -276,4 +276,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return new CodeValidationResult(OperationOutcome.IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
}
}

View File

@ -100,7 +100,7 @@ public interface IValidationSupport
@Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
public class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, OperationOutcome.IssueSeverity> {
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, OperationOutcome.IssueSeverity> {
public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext);
@ -114,6 +114,15 @@ public interface IValidationSupport
super(severity, message, definition);
}
@Override
protected String getDisplay() {
String retVal = null;
if (isOk()) {
retVal = asConceptDefinition().getDisplay();
}
return retVal;
}
}
}

View File

@ -138,4 +138,9 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
}

View File

@ -143,4 +143,15 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theContext, theSystem)) {
return next.lookupCode(theContext, theSystem, theCode);
}
}
return null;
}
}

View File

@ -24,274 +24,284 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
@Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
Set<String> wantCodes = new HashSet<>();
for (ConceptReferenceComponent next : theInclude.getConcept()) {
wantCodes.add(next.getCode());
}
Set<String> wantCodes = new HashSet<>();
for (ConceptReferenceComponent next : theInclude.getConcept()) {
wantCodes.add(next.getCode());
}
CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem());
if (system != null) {
List<ConceptDefinitionComponent> concepts = system.getConcept();
addConcepts(theInclude, retVal, wantCodes, concepts);
}
CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem());
if (system != null) {
List<ConceptDefinitionComponent> concepts = system.getConcept();
addConcepts(theInclude, retVal, wantCodes, concepts);
}
for (UriType next: theInclude.getValueSet()) {
ValueSet vs = myValueSets.get(defaultString(next.getValueAsString()));
if (vs != null) {
for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) {
ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude);
retVal.getContains().addAll(contents.getContains());
}
}
}
for (UriType next : theInclude.getValueSet()) {
ValueSet vs = myValueSets.get(defaultString(next.getValueAsString()));
if (vs != null) {
for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) {
ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude);
retVal.getContains().addAll(contents.getContains());
}
}
}
return retVal;
}
return retVal;
}
private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set<String> theWantCodes, List<ConceptDefinitionComponent> theConcepts) {
for (ConceptDefinitionComponent next : theConcepts) {
if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) {
theRetVal
.addContains()
.setSystem(theInclude.getSystem())
.setCode(next.getCode())
.setDisplay(next.getDisplay());
}
addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept());
}
}
private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set<String> theWantCodes, List<ConceptDefinitionComponent> theConcepts) {
for (ConceptDefinitionComponent next : theConcepts) {
if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) {
theRetVal
.addContains()
.setSystem(theInclude.getSystem())
.setCode(next.getCode())
.setDisplay(next.getDisplay());
}
addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept());
}
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
}
private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) {
synchronized (this) {
Map<String, CodeSystem> codeSystems = myCodeSystems;
Map<String, ValueSet> valueSets = myValueSets;
if (codeSystems == null || valueSets == null) {
codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) {
synchronized (this) {
Map<String, CodeSystem> codeSystems = myCodeSystems;
Map<String, ValueSet> valueSets = myValueSets;
if (codeSystems == null || valueSets == null) {
codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems;
myValueSets = valueSets;
}
myCodeSystems = codeSystems;
myValueSets = valueSets;
}
if (codeSystem) {
return codeSystems.get(theSystem);
} else {
return valueSets.get(theSystem);
}
}
}
if (codeSystem) {
return codeSystems.get(theSystem);
} else {
return valueSets.get(theSystem);
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank");
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank");
if (theClass.equals(StructureDefinition.class)) {
return (T) fetchStructureDefinition(theContext, theUri);
}
if (theClass.equals(StructureDefinition.class)) {
return (T) fetchStructureDefinition(theContext, theUri);
}
if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) {
return (T) fetchValueSet(theContext, theUri);
}
if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) {
return (T) fetchValueSet(theContext, theUri);
}
return null;
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
String url = theUrl;
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
// no change
} else if (url.indexOf('/') == -1) {
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
} else if (StringUtils.countMatches(url, '/') == 1) {
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
}
Map<String, StructureDefinition> map = provideStructureDefinitionMap(theContext);
StructureDefinition retVal = map.get(url);
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
String url = theUrl;
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
// no change
} else if (url.indexOf('/') == -1) {
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
} else if (StringUtils.countMatches(url, '/') == 1) {
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
}
Map<String, StructureDefinition> map = provideStructureDefinitionMap(theContext);
StructureDefinition retVal = map.get(url);
if (retVal == null && url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
String tryUrl = URL_PREFIX_STRUCTURE_DEFINITION + StringUtils.capitalize(url.substring(URL_PREFIX_STRUCTURE_DEFINITION.length()));
retVal = map.get(tryUrl);
}
if (retVal == null && url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
String tryUrl = URL_PREFIX_STRUCTURE_DEFINITION + StringUtils.capitalize(url.substring(URL_PREFIX_STRUCTURE_DEFINITION.length()));
retVal = map.get(tryUrl);
}
return retVal;
}
return retVal;
}
@Override
public ValueSet fetchValueSet(FhirContext theContext, String uri) {
return (ValueSet) fetchCodeSystemOrValueSet(theContext, uri, false);
}
@Override
public ValueSet fetchValueSet(FhirContext theContext, String uri) {
return (ValueSet) fetchCodeSystemOrValueSet(theContext, uri, false);
}
public void flush() {
myCodeSystems = null;
myStructureDefinitions = null;
}
public void flush() {
myCodeSystems = null;
myStructureDefinitions = null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
CodeSystem cs = fetchCodeSystem(theContext, theSystem);
return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
CodeSystem cs = fetchCodeSystem(theContext, theSystem);
return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT;
}
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
InputStreamReader reader = null;
if (inputStream != null) {
try {
reader = new InputStreamReader(inputStream, Charsets.UTF_8);
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
InputStreamReader reader = null;
if (inputStream != null) {
try {
reader = new InputStreamReader(inputStream, Charsets.UTF_8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof CodeSystem) {
CodeSystem nextValueSet = (CodeSystem) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} finally {
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(inputStream);
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof CodeSystem) {
CodeSystem nextValueSet = (CodeSystem) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} finally {
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(inputStream);
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
if (valuesetText != null) {
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
if (valuesetText != null) {
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof StructureDefinition) {
StructureDefinition nextSd = (StructureDefinition) next.getResource();
nextSd.getText().setDivAsString("");
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof StructureDefinition) {
StructureDefinition nextSd = (StructureDefinition) next.getResource();
nextSd.getText().setDivAsString("");
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) {
Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions;
if (structureDefinitions == null) {
structureDefinitions = new HashMap<String, StructureDefinition>();
private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) {
Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions;
if (structureDefinitions == null) {
structureDefinitions = new HashMap<String, StructureDefinition>();
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-others.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-others.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml");
myStructureDefinitions = structureDefinitions;
}
return structureDefinitions;
}
myStructureDefinitions = structureDefinitions;
}
return structureDefinitions;
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
private CodeValidationResult testIfConceptIsInList(CodeSystem theCodeSystem, String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
return testIfConceptIsInListInner(conceptList, theCaseSensitive, code);
}
return testIfConceptIsInListInner(theCodeSystem, conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
private CodeValidationResult testIfConceptIsInListInner(CodeSystem theCodeSystem, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
// recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
// recurse
retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
return retVal;
}
if (retVal != null) {
retVal.setCodeSystemName(theCodeSystem.getName());
retVal.setCodeSystemVersion(theCodeSystem.getVersion());
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
return retVal;
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
if (retVal != null) {
return retVal;
}
}
CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive);
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {

View File

@ -1,43 +1,39 @@
package org.hl7.fhir.dstu3.hapi.ctx;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import java.util.List;
public interface IValidationSupport
extends ca.uhn.fhir.context.support.IContextValidationSupport<ConceptSetComponent, ValueSetExpansionComponent, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
extends ca.uhn.fhir.context.support.IContextValidationSupport<ConceptSetComponent, ValueSetExpansionComponent, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
/**
* Expands the given portion of a ValueSet
*
* @param theInclude
* The portion to include
* @return The expansion
*/
@Override
ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
/**
* Expands the given portion of a ValueSet
*
* @param theInclude The portion to include
* @return The expansion
*/
@Override
ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
/**
* Load and return all possible structure definitions
*/
@Override
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Load and return all possible structure definitions
*/
@Override
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Fetch a code system by Uri
*
* @param uri
* Canonical Uri of the code system
* @param uri Canonical Uri of the code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
@Override
@ -46,82 +42,68 @@ public interface IValidationSupport
/**
* Fetch a valueset by Uri
*
* @param uri
* Canonical Uri of the ValueSet
* @param uri Canonical Uri of the ValueSet
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
ValueSet fetchValueSet(FhirContext theContext, String uri);
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext
* The HAPI FHIR Context object current in use by the validator
* @param theClass
* The type of the resource to load
* @param theUri
* The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
@Override
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
@Override
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
@Override
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
@Override
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem
* The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
@Override
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem
* The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode
* The code, e.g. "<code>1234-5</code>"
* @param theDisplay
* The display name, if it should also be validated
* @return Returns a validation result object
*/
@Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode The code, e.g. "<code>1234-5</code>"
* @param theDisplay The display name, if it should also be validated
* @return Returns a validation result object
*/
@Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
/**
* Generate a snapshot from the given differential profile.
*
* @param theInput
* @param theUrl
* @return Returns null if this module does not know how to handle this request
*/
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName);
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext);
}
public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext);
}
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
super(theSeverity, theMessage);
}
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
super(theSeverity, theMessage);
}
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
super(severity, message, definition);
}
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
super(severity, message, definition);
}
}
@Override
protected String getDisplay() {
String retVal = null;
if (isOk()) {
retVal = asConceptDefinition().getDisplay();
}
return retVal;
}
}
}

View File

@ -257,16 +257,16 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return structureDefinitions;
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
private CodeValidationResult testIfConceptIsInList(CodeSystem theCodeSystem, String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
return testIfConceptIsInListInner(conceptList, theCaseSensitive, code);
return testIfConceptIsInListInner(theCodeSystem, conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
private CodeValidationResult testIfConceptIsInListInner(CodeSystem theCodeSystem, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
@ -279,12 +279,17 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
}
// recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive);
retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
if (retVal != null) {
retVal.setCodeSystemName(theCodeSystem.getName());
retVal.setCodeSystemVersion(theCodeSystem.getVersion());
}
return retVal;
}
@ -297,7 +302,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
@ -307,4 +312,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
}
}

View File

@ -112,6 +112,15 @@ public interface IValidationSupport
super(severity, message, definition);
}
@Override
protected String getDisplay() {
String retVal = null;
if (isOk()) {
retVal = asConceptDefinition().getDisplay();
}
return retVal;
}
}
}

View File

@ -12,6 +12,7 @@ import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
@ -29,10 +30,11 @@ import org.junit.*;
import javax.servlet.ServletException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.*;
public class OperationGenericServer2R4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServer2R4Test.class);
@ -41,6 +43,7 @@ public class OperationGenericServer2R4Test {
private static IdType ourLastId;
private static Object ourLastParam1;
private static Object ourLastParam2;
private static Object ourLastParam3;
private static Parameters ourLastResourceParam;
private int myPort;
private Server myServer;
@ -49,6 +52,7 @@ public class OperationGenericServer2R4Test {
public void before() {
ourLastParam1 = null;
ourLastParam2 = null;
ourLastParam3=null;
ourLastId = null;
ourLastResourceParam = null;
}
@ -112,6 +116,65 @@ public class OperationGenericServer2R4Test {
}
@SuppressWarnings("unchecked")
@Test
public void testDeclarativeStringTypedParameters() throws Exception {
@SuppressWarnings("unused")
class PatientProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Operation(name = "$OP_INSTANCE")
public Parameters opInstance(
@ResourceParam() IBaseResource theResourceParam,
@IdParam IdType theId,
@OperationParam(name = "PARAM1", min = 1, typeName = "uri") IPrimitiveType<String> theParam1,
@OperationParam(name = "PARAM2", min = 1, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theParam2,
@OperationParam(name = "PARAM3", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List<ICompositeType> theParam3
) {
ourLastId = theId;
ourLastParam1 = theParam1;
ourLastParam2 = theParam2;
ourLastParam3 = theParam2;
ourLastResourceParam = (Parameters) theResourceParam;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
}
PatientProvider provider = new PatientProvider();
startServer(provider);
Parameters p = new Parameters();
p.addParameter().setName("PARAM1").setValue(new UriType("PARAM1val"));
p.addParameter().setName("PARAM2").setValue(new StringType("PARAM2val"));
String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p);
HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE");
httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
try (CloseableHttpResponse status = ourClient.execute(httpPost)) {
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
status.getEntity().getContent().close();
UriType param1 = (UriType) ourLastParam1;
assertEquals("PARAM1val", param1.getValue());
List<StringType> param2 = (List<StringType>) ourLastParam2;
assertEquals("PARAM2val", param2.get(0).getValue());
}
}
@Test
public void testDeclarativeTypedParametersInvalid() throws Exception {
@ -138,10 +201,71 @@ public class OperationGenericServer2R4Test {
fail();
} catch (ServletException e) {
ConfigurationException ce = (ConfigurationException) e.getCause();
assertEquals("Failure scanning class PatientProvider: Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PatientProvider.opInstance(org.hl7.fhir.instance.model.api.ICompositeType)", ce.getMessage());
assertThat(ce.getMessage(), containsString("Failure scanning class PatientProvider: Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test"));
}
}
@Test
public void testTypeOperationWithTypeDeclaredByName() throws Exception {
@SuppressWarnings("unused")
class PlainProvider {
@Operation(name = "$OP_INSTANCE", typeName = "Patient", idempotent = true)
public Parameters opInstance(
@ResourceParam() IBaseResource theResourceParam,
@IdParam IdType theId
) {
ourLastId = theId;
Parameters retVal = new Parameters();
retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1"));
return retVal;
}
}
PlainProvider provider = new PlainProvider();
startServer(provider);
HttpGet httpPost = new HttpGet("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE");
try (CloseableHttpResponse status = ourClient.execute(httpPost)) {
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
status.getEntity().getContent().close();
assertEquals("123", ourLastId.getIdPart());
}
}
@Test
public void testTypeOperationWithInvalidType() throws Exception {
@SuppressWarnings("unused")
class PlainProvider {
@Operation(name = "$OP_INSTANCE", typeName = "FOO", idempotent = true)
public Parameters opInstance() {
return null;
}
}
PlainProvider provider = new PlainProvider();
try {
startServer(provider);
fail();
} catch (ServletException e) {
Throwable cause = e.getRootCause();
assertEquals("Failure scanning class PlainProvider: Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage());
}
}
private void startServer(Object theProvider) throws Exception {
myServer = new Server(0);

View File

@ -915,7 +915,7 @@ public class OperationServerR4Test {
}
@Operation(name= "$manualResponseWithPrimitiveParam", idempotent = true, global = true, manualResponse = true)
public void binaryAccess(
public void manualResponseWithPrimitiveParam(
@IdParam IIdType theResourceId,
@OperationParam(name="path", min = 1, max = 1) IPrimitiveType<String> thePath,
ServletRequestDetails theRequestDetails,

View File

@ -70,6 +70,11 @@ public class CachingValidationSupport implements IValidationSupport {
return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return myWrap.lookupCode(theContext, theSystem, theCode);
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return myWrap.generateSnapshot(theInput, theUrl, theName);

View File

@ -185,6 +185,11 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null;

View File

@ -3,8 +3,6 @@ package org.hl7.fhir.dstu3.hapi.validation;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
@ -13,6 +11,8 @@ import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.ElementDefinition;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import java.util.ArrayList;
@ -95,6 +95,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
@Override
public boolean isDatatype(String typeSimple) {

View File

@ -170,6 +170,16 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theContext, theSystem)) {
return next.lookupCode(theContext, theSystem, theCode);
}
}
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
StructureDefinition outcome = null;

View File

@ -75,4 +75,9 @@ public class CachingValidationSupport implements IValidationSupport {
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return myWrap.lookupCode(theContext, theSystem, theCode);
}
}

View File

@ -24,129 +24,129 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
public class PrePopulatedValidationSupport implements IValidationSupport {
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
/**
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<>();
myValueSets = new HashMap<>();
myCodeSystems = new HashMap<>();
}
/**
* Constructor
*/
public PrePopulatedValidationSupport() {
myStructureDefinitions = new HashMap<>();
myValueSets = new HashMap<>();
myCodeSystems = new HashMap<>();
}
/**
* Constructor
*
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
/**
* Constructor
*
* @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and
* values are the resource itself.
* @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
* @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are
* the resource itself.
*/
public PrePopulatedValidationSupport(Map<String, StructureDefinition> theStructureDefinitions, Map<String, ValueSet> theValueSets, Map<String, CodeSystem> theCodeSystems) {
myStructureDefinitions = theStructureDefinitions;
myValueSets = theValueSets;
myCodeSystems = theCodeSystems;
}
/**
* Add a new CodeSystem resource which will be available to the validator. Note that
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addCodeSystem(CodeSystem theCodeSystem) {
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
addToMap(theCodeSystem, myCodeSystems, theCodeSystem.getUrl());
}
/**
* Add a new CodeSystem resource which will be available to the validator. Note that
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addCodeSystem(CodeSystem theCodeSystem) {
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
addToMap(theCodeSystem, myCodeSystems, theCodeSystem.getUrl());
}
/**
* Add a new StructureDefinition resource which will be available to the validator. Note that
* {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addStructureDefinition(StructureDefinition theStructureDefinition) {
Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value");
addToMap(theStructureDefinition, myStructureDefinitions, theStructureDefinition.getUrl());
}
/**
* Add a new StructureDefinition resource which will be available to the validator. Note that
* {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addStructureDefinition(StructureDefinition theStructureDefinition) {
Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value");
addToMap(theStructureDefinition, myStructureDefinitions, theStructureDefinition.getUrl());
}
private <T extends MetadataResource> void addToMap(T theStructureDefinition, Map<String, T> map, String theUrl) {
if (isNotBlank(theUrl)) {
map.put(theUrl, theStructureDefinition);
private <T extends MetadataResource> void addToMap(T theStructureDefinition, Map<String, T> map, String theUrl) {
if (isNotBlank(theUrl)) {
map.put(theUrl, theStructureDefinition);
int lastSlashIdx = theUrl.lastIndexOf('/');
if (lastSlashIdx != -1) {
map.put(theUrl.substring(lastSlashIdx + 1), theStructureDefinition);
int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1);
if (previousSlashIdx != -1) {
map.put(theUrl.substring(previousSlashIdx + 1), theStructureDefinition);
}
}
int lastSlashIdx = theUrl.lastIndexOf('/');
if (lastSlashIdx != -1) {
map.put(theUrl.substring(lastSlashIdx + 1), theStructureDefinition);
int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1);
if (previousSlashIdx != -1) {
map.put(theUrl.substring(previousSlashIdx + 1), theStructureDefinition);
}
}
}
}
}
}
/**
* Add a new ValueSet resource which will be available to the validator. Note that
* {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addValueSet(ValueSet theValueSet) {
Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value");
addToMap(theValueSet, myValueSets, theValueSet.getUrl());
}
/**
* Add a new ValueSet resource which will be available to the validator. Note that
* {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this
* value will be used as the logical URL.
* <p>
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
* it will be stored in three ways:
* <ul>
* <li>Extension</li>
* <li>StructureDefinition/Extension</li>
* <li>http://hl7.org/StructureDefinition/Extension</li>
* </ul>
* </p>
*/
public void addValueSet(ValueSet theValueSet) {
Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value");
addToMap(theValueSet, myValueSets, theValueSet.getUrl());
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(myStructureDefinitions.values());
}
@Override
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String uri) {
return myCodeSystems.get(uri);
}
@ -158,29 +158,29 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theClass.equals(StructureDefinition.class)) {
return (T) myStructureDefinitions.get(theUri);
}
if (theClass.equals(ValueSet.class)) {
return (T) myValueSets.get(theUri);
}
if (theClass.equals(CodeSystem.class)) {
return (T) myCodeSystems.get(theUri);
}
return null;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theClass.equals(StructureDefinition.class)) {
return (T) myStructureDefinitions.get(theUri);
}
if (theClass.equals(ValueSet.class)) {
return (T) myValueSets.get(theUri);
}
if (theClass.equals(CodeSystem.class)) {
return (T) myCodeSystems.get(theUri);
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return myStructureDefinitions.get(theUrl);
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return myStructureDefinitions.get(theUrl);
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
@ -188,8 +188,13 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
}

View File

@ -96,6 +96,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
@Override
public boolean isDatatype(String typeSimple) {

View File

@ -159,6 +159,16 @@ public class ValidationSupportChain implements IValidationSupport {
return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay);
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
for (IValidationSupport next : myChain) {
if (next.isCodeSystemSupported(theContext, theSystem)) {
return next.lookupCode(theContext, theSystem, theCode);
}
}
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<>();

View File

@ -52,7 +52,15 @@
the user has not explicitly configured a preference.
]]>
</action>
<action type="add">
<action type="change">
<![CDATA[
<b>Breaking Change</b>:
The JPA $upload-external-code-system operation has been moved from being a
server level operation (i.e. called on the root of the server) to being
a type level operation (i.e. called on the CodeSystem type).
]]>
</action>
<action type="add">
A new interceptor called
<![CDATA[<code>ConsentInterceptor</code>]]> has been added. This interceptor allows
JPA based servers to make appropriate consent decisions related to resources that
@ -340,6 +348,12 @@
a narrative on an untitled DiagnosticReport were fixed. Thanks to GitHub
user @navyflower for reporting!
</action>
<action type="add">
A new attribute has been added to the @Operation annotation called
<![CDATA[<code>typeName</code>]]>. This annotation can be used to specify a
type for an operation declared on a plain provider without needing to use
a specific version of the FHIR structures.
</action>
</release>
<release version="3.8.0" date="2019-05-30" description="Hippo">
<action type="fix">