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>(); List<Object> plainProviders=new ArrayList<Object>();
plainProviders.add(new PlainProvider()); plainProviders.add(new PlainProvider());
setPlainProviders(plainProviders); registerProviders(plainProviders);
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>(); List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
// ...add some resource providers... // ...add some resource providers...
setResourceProviders(resourceProviders); registerProviders(resourceProviders);
} }
} }

View File

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

View File

@ -79,7 +79,7 @@ public class FhirContext {
private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM; private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap(); private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
private ArrayList<Class<? extends IBase>> myCustomTypes; 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 Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private volatile boolean myInitialized; private volatile boolean myInitialized;
private volatile boolean myInitializing = false; private volatile boolean myInitializing = false;
@ -90,7 +90,7 @@ public class FhirContext {
private volatile INarrativeGenerator myNarrativeGenerator; private volatile INarrativeGenerator myNarrativeGenerator;
private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler(); private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler();
private ParserOptions myParserOptions = new ParserOptions(); 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 Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
private volatile IRestfulClientFactory myRestfulClientFactory; private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
@ -198,7 +198,7 @@ public class FhirContext {
private void ensureCustomTypeList() { private void ensureCustomTypeList() {
myClassToElementDefinition.clear(); myClassToElementDefinition.clear();
if (myCustomTypes == null) { if (myCustomTypes == null) {
myCustomTypes = new ArrayList<Class<? extends IBase>>(); myCustomTypes = new ArrayList<>();
} }
} }
@ -278,14 +278,6 @@ public class FhirContext {
return myNameToElementDefinition.get(theElementName.toLowerCase()); return myNameToElementDefinition.get(theElementName.toLowerCase());
} }
/**
* For unit tests only
*/
int getElementDefinitionCount() {
validateInitialized();
return myClassToElementDefinition.size();
}
/** /**
* Returns all element definitions (resources, datatypes, etc.) * Returns all element definitions (resources, datatypes, etc.)
*/ */
@ -741,21 +733,21 @@ public class FhirContext {
} }
private BaseRuntimeElementDefinition<?> scanDatatype(Class<? extends IElement> theResourceType) { 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); resourceTypes.add(theResourceType);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
return defs.get(theResourceType); return defs.get(theResourceType);
} }
private RuntimeResourceDefinition scanResourceType(Class<? extends IBaseResource> 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); resourceTypes.add(theResourceType);
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes); Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> defs = scanResourceTypes(resourceTypes);
return (RuntimeResourceDefinition) defs.get(theResourceType); return (RuntimeResourceDefinition) defs.get(theResourceType);
} }
private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) { 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) { if (theResourceTypes != null) {
typesToScan.addAll(theResourceTypes); typesToScan.addAll(theResourceTypes);
} }
@ -769,7 +761,7 @@ public class FhirContext {
myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
} }
Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>(); Map<String, BaseRuntimeElementDefinition<?>> nameToElementDefinition = new HashMap<>();
nameToElementDefinition.putAll(myNameToElementDefinition); nameToElementDefinition.putAll(myNameToElementDefinition);
for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) { for (Entry<String, BaseRuntimeElementDefinition<?>> next : scanner.getNameToElementDefinitions().entrySet()) {
if (!nameToElementDefinition.containsKey(next.getKey())) { 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); nameToResourceDefinition.putAll(myNameToResourceDefinition);
for (Entry<String, RuntimeResourceDefinition> next : scanner.getNameToResourceDefinition().entrySet()) { for (Entry<String, RuntimeResourceDefinition> next : scanner.getNameToResourceDefinition().entrySet()) {
if (!nameToResourceDefinition.containsKey(next.getKey())) { 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(myClassToElementDefinition);
classToElementDefinition.putAll(scanner.getClassToElementDefinitions()); classToElementDefinition.putAll(scanner.getClassToElementDefinitions());
for (BaseRuntimeElementDefinition<?> next : classToElementDefinition.values()) { 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(myIdToResourceDefinition);
idToElementDefinition.putAll(scanner.getIdToResourceDefinition()); idToElementDefinition.putAll(scanner.getIdToResourceDefinition());
@ -864,9 +856,9 @@ public class FhirContext {
if (theResourceTypes == null) { if (theResourceTypes == null) {
return 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) { for (Class<? extends IBaseResource> next : theResourceTypes) {
resTypes.add((Class<? extends IElement>) next); resTypes.add(next);
} }
return resTypes; return resTypes;
} }
@ -924,14 +916,14 @@ public class FhirContext {
} }
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) { 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); retVal.add(theResourceType);
return retVal; return retVal;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static List<Class<? extends IBaseResource>> toCollection(Class<?>[] theResourceTypes) { 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) { for (Class<?> clazz : theResourceTypes) {
if (!IResource.class.isAssignableFrom(clazz)) { if (!IResource.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(clazz.getCanonicalName() + " is not an instance of " + IResource.class.getSimpleName()); 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.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.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.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 * 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); 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 { abstract class BaseConceptProperty {
private final String myPropertyName; 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 CDCT myDefinition;
private String myMessage; private String myMessage;
private IST mySeverity; private IST mySeverity;
@ -228,6 +302,190 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
return myDefinition != null; 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(); 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 * {@link IBaseResource} (which is the default) than the operation applies to the server and not to a specific
* resource type. * resource type.
* <p> * <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> * </p>
* @see #typeName() may also be used to specify a value as a String
*/ */
Class<? extends IBaseResource> type() default IBaseResource.class; 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> * 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) * (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") @SuppressWarnings("unchecked")
public static IPrimitiveType<byte[]> getOrCreateData(FhirContext theContext, ICompositeType theAttachment) { public static IPrimitiveType<byte[]> getOrCreateData(FhirContext theContext, ICompositeType theAttachment) {
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "data"); return getOrCreateChild(theContext, theAttachment, "data", "base64Binary");
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;
});
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static IPrimitiveType<String> getOrCreateContentType(FhirContext theContext, ICompositeType theAttachment) { 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); List<IBase> entries = entryChild.getAccessor().getValues(theAttachment);
return entries return entries
.stream() .stream()
.map(t -> (IPrimitiveType<String>) t) .map(t -> (IPrimitiveType<T>) t)
.findFirst() .findFirst()
.orElseGet(() -> { .orElseGet(() -> {
IPrimitiveType<String> string = newPrimitive(theContext, "string", null); IPrimitiveType<String> string = newPrimitive(theContext, theChildDatatype, null);
entryChild.getMutator().setValue(theAttachment, string); 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); List<IBase> valueValues = valueChild.getAccessor().getValues(nextParameter);
valueValues valueValues
.stream() .stream()
.filter(t->t instanceof IPrimitiveType<?>) .filter(t -> t instanceof IPrimitiveType<?>)
.map(t->((IPrimitiveType<?>)t).getValueAsString()) .map(t -> ((IPrimitiveType<?>) t).getValueAsString())
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
.forEach(retVal::add); .forEach(retVal::add);
@ -142,9 +142,99 @@ public class ParametersUtil {
return value; 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) { public static IBaseParameters newInstance(FhirContext theContext) {
Validate.notNull(theContext, "theContext must not be null"); Validate.notNull(theContext, "theContext must not be null");
return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance(); 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; return null;
} }
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override @Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null; return null;

View File

@ -102,4 +102,9 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
return null; 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.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.r4.model.CodeSystem;
import static org.apache.commons.lang3.StringUtils.isBlank; 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..."); ourLog.info("Beginning upload - This may take a while...");
IBaseParameters response = client IBaseParameters response = client
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named(UPLOAD_EXTERNAL_CODE_SYSTEM) .named(UPLOAD_EXTERNAL_CODE_SYSTEM)
.withParameters(inputParameters) .withParameters(inputParameters)
.execute(); .execute();

View File

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

View File

@ -27,6 +27,7 @@ import java.util.*;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import org.apache.commons.codec.binary.StringUtils; import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.instance.hapi.validation.CachingValidationSupport; import org.hl7.fhir.instance.hapi.validation.CachingValidationSupport;
@ -185,13 +186,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
return source; 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) { for (ExpansionContains nextCode : theContains) {
String system = nextCode.getSystem(); String system = nextCode.getSystem();
String code = nextCode.getCode(); String code = nextCode.getCode();
if (theSystem.equals(system) && theCode.equals(code)) { if (theSystem.equals(system) && theCode.equals(code)) {
LookupCodeResult retVal = new LookupCodeResult(); IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult();
retVal.setSearchedForCode(code); retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system); retVal.setSearchedForSystem(system);
retVal.setFound(true); retVal.setFound(true);
@ -210,7 +211,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
} }
@Override @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 haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
@ -236,13 +237,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
for (IIdType nextId : valueSetIds) { for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null, theRequest); ValueSet expansion = expand(nextId, null, theRequest);
List<ExpansionContains> contains = expansion.getExpansion().getContains(); List<ExpansionContains> contains = expansion.getExpansion().getContains();
LookupCodeResult result = lookup(contains, system, code); IContextValidationSupport.LookupCodeResult result = lookup(contains, system, code);
if (result != null) { if (result != null) {
return result; return result;
} }
} }
LookupCodeResult retVal = new LookupCodeResult(); IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult();
retVal.setFound(false); retVal.setFound(false);
retVal.setSearchedForCode(code); retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system); 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.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import java.util.Collections; import javax.annotation.Nonnull;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
* #%L * #%L
@ -43,7 +37,8 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext
List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest); 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); 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.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource; 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; private static final long serialVersionUID = 1L;
MetadataKeyResourcePid(String theValue) { MetadataKeyResourcePid(String theValue) {

View File

@ -20,25 +20,19 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L% * #L%
*/ */
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; 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.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.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 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.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem; 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.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IdType;
@ -52,45 +46,18 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set; 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> { public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class);
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired @Autowired
private ITermCodeSystemDao myCsDao; private ITermCodeSystemDao myCsDao;
@Autowired @Autowired
private ValidationSupportChain myValidationSupport; 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 @Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds; List<IIdType> valueSetIds;
@ -103,7 +70,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
} }
@Override @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 haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.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); ourLog.debug("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(getContext(), system)) { if (myValidationSupport.isCodeSystemSupported(getContext(), system)) {
ourLog.debug("Code system {} is supported", system); ourLog.debug("Code system {} is supported", system);
IContextValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(getContext(), system, code);
CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null);
if (result != null) { if (result != null) {
if (result.isOk()) { return result;
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;
}
}
} }
// We didn't find it.. // We didn't find it..
LookupCodeResult retVal = new LookupCodeResult(); return IContextValidationSupport.LookupCodeResult.notFound(system, code);
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
return retVal;
} }
@Override @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 @Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, 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())) { org.hl7.fhir.r4.model.CodeSystem cs = VersionConvertor_30_40.convertCodeSystem(csDstu3);
String codeSystemUrl = cs.getUrl(); addPidToResource(theEntity, cs);
Long codeSystemResourcePid = retVal.getId();
if (retVal.getDeleted() != null) { myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
// 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);
}
}
return retVal; return retVal;
} }

View File

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

View File

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

View File

@ -20,14 +20,10 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L% * #L%
*/ */
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; 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.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.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil; 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.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; 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.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.model.CodeSystem; 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.CodeableConcept;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set; 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> { public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4.class);
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Autowired @Autowired
private ITermCodeSystemDao myCsDao; private ITermCodeSystemDao myCsDao;
@Autowired @Autowired
@ -76,8 +68,9 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i
return valueSetIds; return valueSetIds;
} }
@Nonnull
@Override @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 haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.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)) { if (myValidationSupport.isCodeSystemSupported(getContext(), system)) {
ourLog.info("Code system {} is supported", system); ourLog.info("Code system {} is supported", system);
IContextValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(getContext(), system, code);
CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null); if (retVal != 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; return retVal;
} }
}
} }
// We didn't find it.. // We didn't find it..
LookupCodeResult retVal = new LookupCodeResult(); return IContextValidationSupport.LookupCodeResult.notFound(system, code);
retVal.setFound(false);
retVal.setSearchedForCode(code);
retVal.setSearchedForSystem(system);
return retVal;
} }
@ -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 @Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
CodeSystem cs = (CodeSystem) theResource; CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource);
if (cs != null && isNotBlank(cs.getUrl())) { myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity);
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);
}
}
}
return retVal; return retVal;
} }

View File

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

View File

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

View File

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

View File

@ -69,6 +69,9 @@ public class TermCodeSystemVersion implements Serializable {
@OneToOne(mappedBy = "myCurrentVersion", optional = true) @OneToOne(mappedBy = "myCurrentVersion", optional = true)
private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny; private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny;
@Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH)
private String myCodeSystemDisplayName;
/** /**
* Constructor * Constructor
*/ */
@ -155,4 +158,14 @@ public class TermCodeSystemVersion implements Serializable {
return result; 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); setCode(theCode);
} }
public TermConcept addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) { public TermConceptParentChildLink addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) {
Validate.notNull(theRelationshipType, "theRelationshipType must not be null"); Validate.notNull(theRelationshipType, "theRelationshipType must not be null");
TermConceptParentChildLink link = new TermConceptParentChildLink(); TermConceptParentChildLink link = new TermConceptParentChildLink();
link.setParent(this); link.setParent(this);
@ -120,7 +120,7 @@ public class TermConcept implements Serializable {
getChildren().add(link); getChildren().add(link);
theChild.getParents().add(link); theChild.getParents().add(link);
return this; return link;
} }
public void addChildren(List<TermConcept> theChildren, RelationshipTypeEnum theRelationshipType) { public void addChildren(List<TermConcept> theChildren, RelationshipTypeEnum theRelationshipType) {

View File

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

View File

@ -20,12 +20,8 @@ package ca.uhn.fhir.jpa.provider;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import ca.uhn.fhir.context.support.IContextValidationSupport;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; 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;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.model.util.JpaConstants;
@ -34,21 +30,27 @@ import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.primitive.*; 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.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 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> { public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2<ValueSet> {
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true) @Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
public ValueSet expand( public ValueSet expand(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@IdParam(optional=true) IdDt theId, @IdParam(optional = true) IdDt theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, @OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier, @OperationParam(name = "identifier", min = 0, max = 1) UriDt theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter, @OperationParam(name = "filter", min = 0, max = 1) StringDt theFilter,
RequestDetails theRequestDetails) { RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart(); boolean haveId = theId != null && theId.hasIdPart();
@ -79,45 +81,29 @@ 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;
}
private String toFilterString(StringDt theFilter) { private String toFilterString(StringDt theFilter) {
return theFilter != null ? theFilter.getValue() : null; return theFilter != null ? theFilter.getValue() : null;
} }
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = {
@OperationParam(name="name", type=StringDt.class, min=1), @OperationParam(name = "name", type = StringDt.class, min = 1),
@OperationParam(name="version", type=StringDt.class, min=0), @OperationParam(name = "version", type = StringDt.class, min = 0),
@OperationParam(name="display", type=StringDt.class, min=1), @OperationParam(name = "display", type = StringDt.class, min = 1),
@OperationParam(name="abstract", type=BooleanDt.class, min=1), @OperationParam(name = "abstract", type = BooleanDt.class, min = 1),
}) })
public Parameters lookup( public Parameters lookup(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeDt theCode, @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name="system", min=0, max=1) UriDt theSystem, @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name="coding", min=0, max=1) CodingDt theCoding, @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
RequestDetails theRequestDetails RequestDetails theRequestDetails
) { ) {
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt>) getDao(); IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt>) getDao();
LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
if (result.isFound()==false) { if (result.isFound() == false) {
throw new ResourceNotFoundException("Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]"); throw new ResourceNotFoundException("Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]");
} }
Parameters retVal = new Parameters(); Parameters retVal = new Parameters();
@ -133,21 +119,20 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
} }
} }
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters= { @OperationParam(name = "result", type = BooleanDt.class, min = 1),
@OperationParam(name="result", type=BooleanDt.class, min=1), @OperationParam(name = "message", type = StringDt.class),
@OperationParam(name="message", type=StringDt.class), @OperationParam(name = "display", type = StringDt.class)
@OperationParam(name="display", type=StringDt.class)
}) })
public Parameters validateCode( public Parameters validateCode(
HttpServletRequest theServletRequest, HttpServletRequest theServletRequest,
@IdParam(optional=true) IdDt theId, @IdParam(optional = true) IdDt theId,
@OperationParam(name="identifier", min=0, max=1) UriDt theValueSetIdentifier, @OperationParam(name = "identifier", min = 0, max = 1) UriDt theValueSetIdentifier,
@OperationParam(name="code", min=0, max=1) CodeDt theCode, @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
@OperationParam(name="system", min=0, max=1) UriDt theSystem, @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
@OperationParam(name="display", min=0, max=1) StringDt theDisplay, @OperationParam(name = "display", min = 0, max = 1) StringDt theDisplay,
@OperationParam(name="coding", min=0, max=1) CodingDt theCoding, @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept, @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConceptDt theCodeableConcept,
RequestDetails theRequestDetails RequestDetails theRequestDetails
) { ) {
@ -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; package ca.uhn.fhir.jpa.provider;
/* import ca.uhn.fhir.jpa.model.util.JpaConstants;
* #%L
* HAPI FHIR JPA Server /**
* %% * @deprecated TerminologyUploaderProvider
* 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%
*/ */
@Deprecated
public class BaseTerminologyUploaderProvider {
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; // FIXME: remove these before 4.0.0
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics; public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM;
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);
public static final String CONCEPT_COUNT = "conceptCount"; public static final String CONCEPT_COUNT = "conceptCount";
public static final String TARGET = "target"; public static final String TARGET = "target";
public static final String SYSTEM = "system";
@Autowired public static final String PARENT_CODE = "parentCode";
private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; public static final String VALUE = "value";
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);
}
}
} }

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

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.dstu3;
* #L% * #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.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -37,37 +38,10 @@ import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class TerminologyUploaderProviderDstu3 extends BaseTerminologyUploaderProvider { /**
* @deprecated Use {@link TerminologyUploaderProvider} instead
@Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { */
@OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) @Deprecated
}) public class TerminologyUploaderProviderDstu3 extends TerminologyUploaderProvider {
public Parameters uploadExternalCodeSystem( // nothing
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);
}
}
} }

View File

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

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4;
* #L% * #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.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails; 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 javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
public class TerminologyUploaderProviderR4 extends BaseTerminologyUploaderProvider { /**
* @deprecated Use {@link TerminologyUploaderProvider} instead
@Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { */
@OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) public class TerminologyUploaderProviderR4 extends TerminologyUploaderProvider {
}) // nothing
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);
}
} }

View File

@ -21,10 +21,8 @@ package ca.uhn.fhir.jpa.term;
*/ */
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; 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.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
@ -261,6 +260,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource); 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); protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap);
abstract void createOrUpdateValueSet(ValueSet theValueSet); abstract void createOrUpdateValueSet(ValueSet theValueSet);
@ -767,9 +770,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Override @Override
public Optional<TermConcept> findCode(String theCodeSystem, String theCode) { public Optional<TermConcept> findCode(String theCodeSystem, String 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); TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem);
return myConceptDao.findByCodeSystemAndCode(csv, theCode); return myConceptDao.findByCodeSystemAndCode(csv, theCode);
});
} }
@Override @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) { private int saveConcept(TermConcept theConcept) {
int retVal = 0; int retVal = 0;
@ -1124,7 +1166,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Override @Override
@Transactional(propagation = Propagation.REQUIRED) @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"); ourLog.info("Storing code system");
ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied"); ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied");
@ -1170,6 +1212,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
} }
theCodeSystemVersion.setCodeSystem(codeSystem); 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)"); ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
// Validate the code system // Validate the code system
@ -1226,8 +1271,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
ourLog.info("CodeSystem resource has ID: {}", csId.getValue()); ourLog.info("CodeSystem resource has ID: {}", csId.getValue());
theCodeSystemVersion.setResource(resource); populateCodeSystemVersionProperties(theCodeSystemVersion, theCodeSystemResource, resource);
storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion);
storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemResource.getVersion(), theCodeSystemVersion);
myDeferredConceptMaps.addAll(theConceptMaps); myDeferredConceptMaps.addAll(theConceptMaps);
myDeferredValueSets.addAll(theValueSets); myDeferredValueSets.addAll(theValueSets);
@ -1235,6 +1281,99 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return csId; 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 @Override
@Transactional @Transactional
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) { public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
@ -1443,6 +1582,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
} }
@Override @Override
@Transactional
public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) {
VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA);
VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB);
@ -1471,6 +1611,136 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes); 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 private @Nullable
ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) { ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) {
QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get(); QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
@ -1608,7 +1878,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
predicates = new ArrayList<>(); predicates = new ArrayList<>();
coding = translationQuery.getCoding(); coding = translationQuery.getCoding();
String targetCode = null; String targetCode;
String targetCodeSystem = null; String targetCodeSystem = null;
if (coding.hasCode()) { if (coding.hasCode()) {
predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode()));
@ -1716,12 +1986,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return retVal; return retVal;
} }
private void verifyNoDuplicates(Collection<TermConcept> theConcepts, Set<String> theCodes) { private static void extractLinksFromConceptAndChildren(TermConcept theConcept, List<TermConceptParentChildLink> theLinks) {
for (TermConcept next : theConcepts) { theLinks.addAll(theConcept.getParents());
if (!theCodes.add(next.getCode())) { for (TermConceptParentChildLink child : theConcept.getChildren()) {
throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes"); extractLinksFromConceptAndChildren(child.getChild(), theLinks);
}
verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes);
} }
} }
@ -1775,4 +2043,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) { public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) {
ourForceSaveDeferredAlwaysForUnitTest = 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.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -61,6 +64,8 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
@Autowired @Autowired
private IHapiTerminologySvc myTerminologySvc; private IHapiTerminologySvc myTerminologySvc;
@Autowired
private PlatformTransactionManager myTransactionManager;
/** /**
* Constructor * Constructor
@ -100,6 +105,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
} catch (FHIRException e) { } catch (FHIRException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
validateCodeSystemForStorage(theCodeSystemResource);
if (isBlank(resourceToStore.getIdElement().getIdPart())) { if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId(); return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId();
@ -217,14 +223,14 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
return null; return null;
} }
@CoverageIgnore
@Override @Override
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) { public IBaseResource fetchResource(FhirContext theContext, Class theClass, String theUri) {
return null; return null;
} }
@CoverageIgnore
@Override @Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) { public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
return null; return null;
} }
@ -293,20 +299,30 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
@CoverageIgnore @CoverageIgnore
@Override @Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { 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); Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) { if (codeOpt.isPresent()) {
ConceptDefinitionComponent def = new ConceptDefinitionComponent(); ConceptDefinitionComponent def = new ConceptDefinitionComponent();
TermConcept code = codeOpt.get(); TermConcept code = codeOpt.get();
def.setCode(code.getCode()); def.setCode(code.getCode());
def.setDisplay(code.getDisplay()); def.setDisplay(code.getDisplay());
CodeValidationResult retVal = new CodeValidationResult(def); IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties()); retVal.setProperties(code.toValidationProperties());
retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName()); retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
return retVal; 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 @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.jpa.entity.TermConcept;
import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil; 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.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; 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.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -48,6 +52,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/ */
public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements IHapiTerminologySvcR4 { public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements IHapiTerminologySvcR4 {
@Autowired @Autowired
@Qualifier("myConceptMapDaoR4") @Qualifier("myConceptMapDaoR4")
private IFhirResourceDao<ConceptMap> myConceptMapResourceDao; private IFhirResourceDao<ConceptMap> myConceptMapResourceDao;
@ -60,7 +65,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Autowired @Autowired
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
@Autowired @Autowired
private IHapiTerminologySvc myTerminologySvc; private PlatformTransactionManager myTransactionManager;
private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) { private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
if (isNotBlank(theCode.getCode())) { if (isNotBlank(theCode.getCode())) {
@ -87,6 +92,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override @Override
protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
validateCodeSystemForStorage(theCodeSystemResource);
if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId(); return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId();
@ -227,7 +233,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@Override @Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return myTerminologySvc.supportsSystem(theSystem); return supportsSystem(theSystem);
} }
@Override @Override
@ -237,19 +243,28 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
@CoverageIgnore @CoverageIgnore
@Override @Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode); TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txTemplate.execute(t-> {
Optional<TermConcept> codeOpt = findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) { if (codeOpt.isPresent()) {
TermConcept code = codeOpt.get(); TermConcept code = codeOpt.get();
ConceptDefinitionComponent def = new ConceptDefinitionComponent(); ConceptDefinitionComponent def = new ConceptDefinitionComponent();
def.setCode(code.getCode()); def.setCode(code.getCode());
def.setDisplay(code.getDisplay()); def.setDisplay(code.getDisplay());
CodeValidationResult retVal = new CodeValidationResult(def); IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties()); retVal.setProperties(code.toValidationProperties());
return retVal; 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.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; 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.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/* /*
* #%L * #%L
@ -76,13 +79,15 @@ public interface IHapiTerminologySvc {
*/ */
void setProcessDeferred(boolean theProcessDeferred); 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 * @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); 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 deleteConceptMapAndChildren(ResourceTable theResourceTable);
void deleteValueSetAndChildren(ResourceTable theResourceTable); void deleteValueSetAndChildren(ResourceTable theResourceTable);
@ -98,4 +103,8 @@ public interface IHapiTerminologySvc {
List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest); List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest);
IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB); 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; 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.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
@ -63,6 +63,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("ACME Codes");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); 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"); TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA");
parentC.addChild(childCA, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -129,7 +130,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
parentB.addChild(childI, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -165,7 +166,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -489,13 +490,17 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
createExternalCsAndLocalVs(); createExternalCsAndLocalVs();
// We're making sure that a reindex doesn't wipe out all of the
// stored codes in the terminology service
myResourceReindexingSvc.markAllResourcesForReindexing(); myResourceReindexingSvc.markAllResourcesForReindexing();
myResourceReindexingSvc.forceReindexingPass(); myResourceReindexingSvc.forceReindexingPass();
myResourceReindexingSvc.forceReindexingPass(); myResourceReindexingSvc.forceReindexingPass();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred(); IContextValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd);
myHapiTerminologySvc.saveDeferred(); assertEquals(true, lookupResults.isFound());
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet(); ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude(); ConceptSetComponent include = vs.getCompose().addInclude();
@ -680,11 +685,11 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
cs.setResource(table); cs.setResource(table);
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA); 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 code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct"); 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()); 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.subscription.module.cache.SubscriptionRegistry;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl; import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; 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.ResourceCountCache;
import ca.uhn.fhir.jpa.util.ResourceProviderFactory; import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
@ -127,6 +128,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired @Autowired
protected ITermCodeSystemDao myTermCodeSystemDao; protected ITermCodeSystemDao myTermCodeSystemDao;
@Autowired @Autowired
protected ITermConceptParentChildLinkDao myTermConceptParentChildLinkDao;
@Autowired
protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao; protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao;
@Autowired @Autowired
@Qualifier("myCompartmentDefinitionDaoR4") @Qualifier("myCompartmentDefinitionDaoR4")
@ -288,7 +291,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Qualifier("myTaskDaoR4") @Qualifier("myTaskDaoR4")
protected IFhirResourceDao<Task> myTaskDao; protected IFhirResourceDao<Task> myTaskDao;
@Autowired @Autowired
protected IHapiTerminologySvc myTermSvc; protected IHapiTerminologySvcR4 myTermSvc;
@Autowired @Autowired
protected PlatformTransactionManager myTransactionMgr; protected PlatformTransactionManager myTransactionMgr;
@Autowired @Autowired

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4; 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.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; 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"); TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA");
parentC.addChild(childCA, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -132,7 +132,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -163,7 +163,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
parentB.addChild(childI, RelationshipTypeEnum.ISA); 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; return codeSystem;
} }
@ -476,7 +476,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
concept = new TermConcept(cs, "LA9999-7"); concept = new TermConcept(cs, "LA9999-7");
cs.getConcepts().add(concept); 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 valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET); valueSet.setUrl(URL_MY_VALUE_SET);
@ -803,11 +803,11 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
cs.setResource(table); cs.setResource(table);
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA); 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 code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct"); 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()); assertEquals(true, outcome.isFound());
} }

View File

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

View File

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

View File

@ -85,7 +85,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try { try {
ourClient ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO"))
.andParameter("package", new Attachment().setUrl("foo").setData(packageBytes)) .andParameter("package", new Attachment().setUrl("foo").setData(packageBytes))
@ -102,7 +102,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient Parameters respParam = ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .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); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp); 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 * Try uploading a second time
@ -119,7 +119,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
respParam = ourClient respParam = ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes))
@ -136,7 +136,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try { try {
ourClient ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("package", new Attachment().setData(packageBytes)) .andParameter("package", new Attachment().setData(packageBytes))
@ -154,7 +154,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try { try {
ourClient ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "package", new Attachment().setUrl("foo").setData(packageBytes)) .withParameter(Parameters.class, "package", new Attachment().setUrl("foo").setData(packageBytes))
.execute(); .execute();
@ -170,7 +170,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
try { try {
ourClient ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.execute(); .execute();
@ -186,7 +186,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient Parameters respParam = ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .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); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp); ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
} }
@Test @Test
@ -212,7 +212,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
Parameters respParam = ourClient Parameters respParam = ourClient
.operation() .operation()
.onServer() .onType(CodeSystem.class)
.named("upload-external-code-system") .named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI))
.andParameter("localfile", new StringType(tempFile.getAbsolutePath())) .andParameter("localfile", new StringType(tempFile.getAbsolutePath()))
@ -221,7 +221,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp); ourLog.info(resp);
assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1));
} }
@AfterClass @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.dao.r4.FhirResourceDaoR4TerminologyTest;
import ca.uhn.fhir.jpa.model.util.JpaConstants; 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.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
@ -14,8 +16,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.assertEquals; import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.fail; import static org.junit.Assert.*;
public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test { 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 @Test
public void testLookupOnExternalCode() { public void testLookupOnExternalCode() {
ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd); ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd);
@ -61,11 +150,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("SYSTEM VERSION", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); 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 // With HTTP GET
respParam = ourClient respParam = ourClient
@ -81,11 +172,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals(("SYSTEM VERSION"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); 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); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); 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 @Test
@ -140,7 +235,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); 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("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -176,7 +271,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); 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("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
@ -245,11 +340,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("v3.MaritalStatus", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("version", respParam.getParameter().get(1).getName());
assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("2018-08-12", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue()); 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 @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.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; 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.ContentType;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -514,7 +513,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
ourLog.info(resp); ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName()); 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()); assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded")); 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"); TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB); 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; return codeSystem;
} }

View File

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

View File

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

View File

@ -1,38 +1,22 @@
package ca.uhn.fhir.jpa.term; 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.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil; 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.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.io.IOException; 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.containsString;
import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class TerminologyLoaderSvcImgthlaTest { public class TerminologyLoaderSvcImgthlaTest {

View File

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

View File

@ -52,6 +52,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL); codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("SYSTEM NAME");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
@ -95,7 +96,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermConcept parentB = new TermConcept(cs, "ParentB"); TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(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; return id;
} }
@ -114,7 +115,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermConcept parentA = new TermConcept(cs, "CS2"); TermConcept parentA = new TermConcept(cs, "CS2");
cs.getConcepts().add(parentA); 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; return id;
} }
@ -146,7 +147,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
code.addPropertyString("HELLO", "12345-2"); code.addPropertyString("HELLO", "12345-2");
cs.getConcepts().add(code); 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(); TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table); cs.setResource(table);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs);
// Update // Update
cs = new TermCodeSystemVersion(); cs = new TermCodeSystemVersion();
@ -171,17 +172,14 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified();
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
cs.setResource(table); 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 // Try to update to a different resource
codeSystem = new CodeSystem(); codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL); codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
cs.setResource(table);
try { try {
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
fail(); fail();
} catch (UnprocessableEntityException e) { } 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/")); 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); child.addChild(parent, RelationshipTypeEnum.ISA);
try { try {
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", cs); myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION" , cs);
fail(); fail();
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); assertEquals("CodeSystem contains circular reference around code parent", e.getMessage());
@ -708,7 +706,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
runInTransaction(()->{ runInTransaction(()->{
ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong()); ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong());
version.setResource(resTable); 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(); 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; 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.DaoConfig;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; 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.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType; 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.*;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
import org.junit.*; import org.junit.*;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.mockito.Mock; import org.mockito.Mock;
@ -18,9 +26,12 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.*;
@ -31,13 +42,12 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class); private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class);
@Rule @Rule
public final ExpectedException expectedException = ExpectedException.none(); public final ExpectedException expectedException = ExpectedException.none();
@Mock
IValueSetCodeAccumulator myValueSetCodeAccumulator;
private IIdType myConceptMapId; private IIdType myConceptMapId;
private IIdType myExtensionalCsId; private IIdType myExtensionalCsId;
private IIdType myExtensionalVsId; private IIdType myExtensionalVsId;
@Mock
IValueSetCodeAccumulator myValueSetCodeAccumulator;
@Before @Before
public void before() { public void before() {
myDaoConfig.setAllowExternalReferences(true); myDaoConfig.setAllowExternalReferences(true);
@ -53,6 +63,8 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL); codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
codeSystem.setName("SYSTEM NAME");
codeSystem.setVersion("SYSTEM VERSION");
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
@ -95,7 +107,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
TermConcept parentB = new TermConcept(cs, "ParentB"); TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(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; return id;
} }
@ -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 @Test
public void testCreateConceptMapWithMissingSourceSystem() { public void testCreateConceptMapWithMissingSourceSystem() {
ConceptMap conceptMap = new ConceptMap(); ConceptMap conceptMap = new ConceptMap();
@ -183,7 +493,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
@Test @Test
public void testCreateConceptMapWithVirtualSourceSystem() { public void testCreateConceptMapWithVirtualSourceSystem() {
ConceptMap conceptMap = createConceptMap(); 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")); conceptMap.setSource(new CanonicalType("http://hl7.org/fhir/uv/livd/StructureDefinition/loinc-livd"));
persistConceptMap(conceptMap); persistConceptMap(conceptMap);
@ -283,6 +593,17 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
verify(myValueSetCodeAccumulator, times(9)).includeCodeWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); 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 @Test
public void testStoreTermCodeSystemAndChildren() throws Exception { public void testStoreTermCodeSystemAndChildren() throws Exception {
loadAndPersistCodeSystemWithDesignations(); loadAndPersistCodeSystemWithDesignations();

View File

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

View File

@ -23,6 +23,17 @@ package ca.uhn.fhir.jpa.model.util;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants { 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 * Operation name for the $expunge operation
*/ */
@ -158,6 +169,11 @@ public class JpaConstants {
*/ */
public static final String OPERATION_BINARY_ACCESS_WRITE = "$binary-access-write"; 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> * <p>
* This extension should be of type <code>string</code> and should be * This extension should be of type <code>string</code> and should be

View File

@ -1,7 +1,6 @@
package ca.uhn.fhirtest.interceptor; package ca.uhn.fhirtest.interceptor;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider; 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.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
@ -35,7 +34,9 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
if (isBlank(authHeader)) { if (isBlank(authHeader)) {
return new RuleBuilder() return new RuleBuilder()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andAllowAllResponses().andThen() .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).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andAllowAllResponses().andThen() .deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().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); 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) { private static String toLogString(Class<?> theType) {
if (theType == null) { if (theType == null) {
return null; return null;

View File

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

View File

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

View File

@ -187,7 +187,7 @@ public class OperationParameter implements IParameter {
mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType); mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType);
myConverter = new OperationParamConverter(); myConverter = new OperationParamConverter();
} else { } 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, public ValidateMethodBindingDstu2Plus(Class<?> theReturnResourceType, Class<? extends IBaseResource> theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider,
Validate theAnnotation) { 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>(); List<IParameter> newParams = new ArrayList<IParameter>();
int idx = 0; int idx = 0;

View File

@ -276,4 +276,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return new CodeValidationResult(OperationOutcome.IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); 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 @Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); 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) { public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext); super(theNext);
@ -114,6 +114,15 @@ public interface IValidationSupport
super(severity, message, definition); 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; 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); 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

@ -49,7 +49,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
addConcepts(theInclude, retVal, wantCodes, concepts); addConcepts(theInclude, retVal, wantCodes, concepts);
} }
for (UriType next: theInclude.getValueSet()) { for (UriType next : theInclude.getValueSet()) {
ValueSet vs = myValueSets.get(defaultString(next.getValueAsString())); ValueSet vs = myValueSets.get(defaultString(next.getValueAsString()));
if (vs != null) { if (vs != null) {
for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) { for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) {
@ -243,16 +243,16 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return structureDefinitions; 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; String code = theCode;
if (theCaseSensitive == false) { if (theCaseSensitive == false) {
code = code.toUpperCase(); 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; CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) { for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode(); String nextCandidate = next.getCode();
@ -265,12 +265,17 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
} }
// recurse // recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive); retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive);
if (retVal != null) { if (retVal != null) {
break; break;
} }
} }
if (retVal != null) {
retVal.setCodeSystemName(theCodeSystem.getName());
retVal.setCodeSystemVersion(theCodeSystem.getVersion());
}
return retVal; return retVal;
} }
@ -283,7 +288,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
caseSensitive = cs.getCaseSensitive(); caseSensitive = cs.getCaseSensitive();
} }
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive); CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive);
if (retVal != null) { if (retVal != null) {
return retVal; return retVal;
@ -293,6 +298,11 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); 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 @Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
return null; return null;

View File

@ -1,18 +1,16 @@
package org.hl7.fhir.dstu3.hapi.ctx; 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;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; 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 org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext; import java.util.List;
import ca.uhn.fhir.context.support.IContextValidationSupport;
public interface IValidationSupport 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> {
@ -20,8 +18,7 @@ public interface IValidationSupport
/** /**
* Expands the given portion of a ValueSet * Expands the given portion of a ValueSet
* *
* @param theInclude * @param theInclude The portion to include
* The portion to include
* @return The expansion * @return The expansion
*/ */
@Override @Override
@ -36,8 +33,7 @@ public interface IValidationSupport
/** /**
* Fetch a code system by Uri * Fetch a code system by Uri
* *
* @param uri * @param uri Canonical Uri of the code system
* Canonical Uri of the code system
* @return The valueset (must not be null, but can be an empty ValueSet) * @return The valueset (must not be null, but can be an empty ValueSet)
*/ */
@Override @Override
@ -46,28 +42,11 @@ public interface IValidationSupport
/** /**
* Fetch a valueset by Uri * Fetch a valueset by Uri
* *
* @param uri * @param uri Canonical Uri of the ValueSet
* Canonical Uri of the ValueSet
* @return The valueset (must not be null, but can be an empty ValueSet) * @return The valueset (must not be null, but can be an empty ValueSet)
*/ */
ValueSet fetchValueSet(FhirContext theContext, String uri); 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 @Override
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl); StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
@ -75,8 +54,7 @@ public interface IValidationSupport
* Returns <code>true</code> if codes in the given code system can be expanded * Returns <code>true</code> if codes in the given code system can be expanded
* or validated * or validated
* *
* @param theSystem * @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
* 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 * @return Returns <code>true</code> if codes in the given code system can be
* validated * validated
*/ */
@ -88,12 +66,9 @@ public interface IValidationSupport
* name. This method is called to check codes which are found in "example" * 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. * binding fields (e.g. <code>Observation.code</code> in the default profile.
* *
* @param theCodeSystem * @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* The code system, e.g. "<code>http://loinc.org</code>" * @param theCode The code, e.g. "<code>1234-5</code>"
* @param theCode * @param theDisplay The display name, if it should also be validated
* 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 * @return Returns a validation result object
*/ */
@Override @Override
@ -102,8 +77,6 @@ public interface IValidationSupport
/** /**
* Generate a snapshot from the given differential profile. * 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 * @return Returns null if this module does not know how to handle this request
*/ */
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName); StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName);
@ -122,6 +95,15 @@ public interface IValidationSupport
super(severity, message, 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; 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; String code = theCode;
if (theCaseSensitive == false) { if (theCaseSensitive == false) {
code = code.toUpperCase(); 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; CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) { for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode(); String nextCandidate = next.getCode();
@ -279,12 +279,17 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
} }
// recurse // recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive); retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive);
if (retVal != null) { if (retVal != null) {
break; break;
} }
} }
if (retVal != null) {
retVal.setCodeSystemName(theCodeSystem.getName());
retVal.setCodeSystemVersion(theCodeSystem.getVersion());
}
return retVal; return retVal;
} }
@ -297,7 +302,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
caseSensitive = cs.getCaseSensitive(); caseSensitive = cs.getCaseSensitive();
} }
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive); CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive);
if (retVal != null) { if (retVal != null) {
return retVal; return retVal;
@ -307,4 +312,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); 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); 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 ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse; 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.HttpPost;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
@ -29,10 +30,11 @@ import org.junit.*;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals; import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.fail; import static org.junit.Assert.*;
public class OperationGenericServer2R4Test { public class OperationGenericServer2R4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServer2R4Test.class); 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 IdType ourLastId;
private static Object ourLastParam1; private static Object ourLastParam1;
private static Object ourLastParam2; private static Object ourLastParam2;
private static Object ourLastParam3;
private static Parameters ourLastResourceParam; private static Parameters ourLastResourceParam;
private int myPort; private int myPort;
private Server myServer; private Server myServer;
@ -49,6 +52,7 @@ public class OperationGenericServer2R4Test {
public void before() { public void before() {
ourLastParam1 = null; ourLastParam1 = null;
ourLastParam2 = null; ourLastParam2 = null;
ourLastParam3=null;
ourLastId = null; ourLastId = null;
ourLastResourceParam = 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 @Test
public void testDeclarativeTypedParametersInvalid() throws Exception { public void testDeclarativeTypedParametersInvalid() throws Exception {
@ -138,10 +201,71 @@ public class OperationGenericServer2R4Test {
fail(); fail();
} catch (ServletException e) { } catch (ServletException e) {
ConfigurationException ce = (ConfigurationException) e.getCause(); 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 { private void startServer(Object theProvider) throws Exception {
myServer = new Server(0); myServer = new Server(0);

View File

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

View File

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

View File

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

View File

@ -3,8 +3,6 @@ package org.hl7.fhir.dstu3.hapi.validation;
import ca.uhn.fhir.context.*; import ca.uhn.fhir.context.*;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import org.apache.commons.lang3.Validate; 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.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.context.IWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; 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.ElementDefinition;
import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet; 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 org.hl7.fhir.utilities.validation.ValidationMessage;
import java.util.ArrayList; import java.util.ArrayList;
@ -95,6 +95,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport {
return null; return null;
} }
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider { private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
@Override @Override
public boolean isDatatype(String typeSimple) { 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); 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 @Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) { public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
StructureDefinition outcome = null; 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) { public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return myWrap.validateCode(theContext, theCodeSystem, theCode, 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

@ -192,4 +192,9 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
return null; 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; return null;
} }
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider { private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider {
@Override @Override
public boolean isDatatype(String typeSimple) { 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); 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 @Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) { public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
ArrayList<StructureDefinition> retVal = new ArrayList<>(); ArrayList<StructureDefinition> retVal = new ArrayList<>();

View File

@ -52,6 +52,14 @@
the user has not explicitly configured a preference. the user has not explicitly configured a preference.
]]> ]]>
</action> </action>
<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"> <action type="add">
A new interceptor called A new interceptor called
<![CDATA[<code>ConsentInterceptor</code>]]> has been added. This interceptor allows <![CDATA[<code>ConsentInterceptor</code>]]> has been added. This interceptor allows
@ -340,6 +348,12 @@
a narrative on an untitled DiagnosticReport were fixed. Thanks to GitHub a narrative on an untitled DiagnosticReport were fixed. Thanks to GitHub
user @navyflower for reporting! user @navyflower for reporting!
</action> </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>
<release version="3.8.0" date="2019-05-30" description="Hippo"> <release version="3.8.0" date="2019-05-30" description="Hippo">
<action type="fix"> <action type="fix">