diff --git a/examples/src/main/java/example/ExampleProviders.java b/examples/src/main/java/example/ExampleProviders.java index a17c3b3103b..8a41d40a469 100644 --- a/examples/src/main/java/example/ExampleProviders.java +++ b/examples/src/main/java/example/ExampleProviders.java @@ -48,11 +48,11 @@ public class ExampleServlet extends ca.uhn.fhir.rest.server.RestfulServer { */ List plainProviders=new ArrayList(); plainProviders.add(new PlainProvider()); - setPlainProviders(plainProviders); + registerProviders(plainProviders); List resourceProviders = new ArrayList(); // ...add some resource providers... - setResourceProviders(resourceProviders); + registerProviders(resourceProviders); } } diff --git a/examples/src/main/java/example/ValidatorExamples.java b/examples/src/main/java/example/ValidatorExamples.java index c8c03d961f5..270967f5417 100644 --- a/examples/src/main/java/example/ValidatorExamples.java +++ b/examples/src/main/java/example/ValidatorExamples.java @@ -264,6 +264,12 @@ public class ValidatorExamples { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + // TODO: implement + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { // TODO: implement diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 0e693225878..0af50fb96a0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -79,7 +79,7 @@ public class FhirContext { private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM; private volatile Map, BaseRuntimeElementDefinition> myClassToElementDefinition = Collections.emptyMap(); private ArrayList> myCustomTypes; - private Map> myDefaultTypeForProfile = new HashMap>(); + private Map> myDefaultTypeForProfile = new HashMap<>(); private volatile Map myIdToResourceDefinition = Collections.emptyMap(); private volatile boolean myInitialized; private volatile boolean myInitializing = false; @@ -90,7 +90,7 @@ public class FhirContext { private volatile INarrativeGenerator myNarrativeGenerator; private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler(); private ParserOptions myParserOptions = new ParserOptions(); - private Set myPerformanceOptions = new HashSet(); + private Set myPerformanceOptions = new HashSet<>(); private Collection> myResourceTypesToScan; private volatile IRestfulClientFactory myRestfulClientFactory; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; @@ -198,7 +198,7 @@ public class FhirContext { private void ensureCustomTypeList() { myClassToElementDefinition.clear(); if (myCustomTypes == null) { - myCustomTypes = new ArrayList>(); + myCustomTypes = new ArrayList<>(); } } @@ -278,14 +278,6 @@ public class FhirContext { return myNameToElementDefinition.get(theElementName.toLowerCase()); } - /** - * For unit tests only - */ - int getElementDefinitionCount() { - validateInitialized(); - return myClassToElementDefinition.size(); - } - /** * Returns all element definitions (resources, datatypes, etc.) */ @@ -741,21 +733,21 @@ public class FhirContext { } private BaseRuntimeElementDefinition scanDatatype(Class theResourceType) { - ArrayList> resourceTypes = new ArrayList>(); + ArrayList> resourceTypes = new ArrayList<>(); resourceTypes.add(theResourceType); Map, BaseRuntimeElementDefinition> defs = scanResourceTypes(resourceTypes); return defs.get(theResourceType); } private RuntimeResourceDefinition scanResourceType(Class theResourceType) { - ArrayList> resourceTypes = new ArrayList>(); + ArrayList> resourceTypes = new ArrayList<>(); resourceTypes.add(theResourceType); Map, BaseRuntimeElementDefinition> defs = scanResourceTypes(resourceTypes); return (RuntimeResourceDefinition) defs.get(theResourceType); } private synchronized Map, BaseRuntimeElementDefinition> scanResourceTypes(Collection> theResourceTypes) { - List> typesToScan = new ArrayList>(); + List> typesToScan = new ArrayList<>(); if (theResourceTypes != null) { typesToScan.addAll(theResourceTypes); } @@ -769,7 +761,7 @@ public class FhirContext { myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition(); } - Map> nameToElementDefinition = new HashMap>(); + Map> nameToElementDefinition = new HashMap<>(); nameToElementDefinition.putAll(myNameToElementDefinition); for (Entry> next : scanner.getNameToElementDefinitions().entrySet()) { if (!nameToElementDefinition.containsKey(next.getKey())) { @@ -777,7 +769,7 @@ public class FhirContext { } } - Map nameToResourceDefinition = new HashMap(); + Map nameToResourceDefinition = new HashMap<>(); nameToResourceDefinition.putAll(myNameToResourceDefinition); for (Entry next : scanner.getNameToResourceDefinition().entrySet()) { if (!nameToResourceDefinition.containsKey(next.getKey())) { @@ -785,7 +777,7 @@ public class FhirContext { } } - Map, BaseRuntimeElementDefinition> classToElementDefinition = new HashMap, BaseRuntimeElementDefinition>(); + Map, BaseRuntimeElementDefinition> classToElementDefinition = new HashMap<>(); classToElementDefinition.putAll(myClassToElementDefinition); classToElementDefinition.putAll(scanner.getClassToElementDefinitions()); for (BaseRuntimeElementDefinition next : classToElementDefinition.values()) { @@ -798,7 +790,7 @@ public class FhirContext { } } - Map idToElementDefinition = new HashMap(); + Map idToElementDefinition = new HashMap<>(); idToElementDefinition.putAll(myIdToResourceDefinition); idToElementDefinition.putAll(scanner.getIdToResourceDefinition()); @@ -864,9 +856,9 @@ public class FhirContext { if (theResourceTypes == null) { return null; } - List> resTypes = new ArrayList>(); + List> resTypes = new ArrayList<>(); for (Class next : theResourceTypes) { - resTypes.add((Class) next); + resTypes.add(next); } return resTypes; } @@ -924,14 +916,14 @@ public class FhirContext { } private static Collection> toCollection(Class theResourceType) { - ArrayList> retVal = new ArrayList>(1); + ArrayList> retVal = new ArrayList<>(1); retVal.add(theResourceType); return retVal; } @SuppressWarnings("unchecked") private static List> toCollection(Class[] theResourceTypes) { - ArrayList> retVal = new ArrayList>(1); + ArrayList> retVal = new ArrayList<>(1); for (Class clazz : theResourceTypes) { if (!IResource.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(clazz.getCanonicalName() + " is not an instance of " + IResource.class.getSimpleName()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java index d3f5263dbc7..38f34da40ba 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java @@ -21,9 +21,21 @@ package ca.uhn.fhir.context.support; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.util.ParametersUtil; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * This interface is a version-independent representation of the @@ -102,6 +114,68 @@ public interface IContextValidationSupport */ CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); + /** + * Look up a code using the system and code value + * + * @param theContext The FHIR context + * @param theSystem The CodeSystem URL + * @param theCode The code + */ + LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode); + + class ConceptDesignation { + private String myLanguage; + private String myUseSystem; + private String myUseCode; + private String myUseDisplay; + private String myValue; + + public String getLanguage() { + return myLanguage; + } + + public ConceptDesignation setLanguage(String theLanguage) { + myLanguage = theLanguage; + return this; + } + + public String getUseSystem() { + return myUseSystem; + } + + public ConceptDesignation setUseSystem(String theUseSystem) { + myUseSystem = theUseSystem; + return this; + } + + public String getUseCode() { + return myUseCode; + } + + public ConceptDesignation setUseCode(String theUseCode) { + myUseCode = theUseCode; + return this; + } + + public String getUseDisplay() { + return myUseDisplay; + } + + public ConceptDesignation setUseDisplay(String theUseDisplay) { + myUseDisplay = theUseDisplay; + return this; + } + + public String getValue() { + return myValue; + } + + public ConceptDesignation setValue(String theValue) { + myValue = theValue; + return this; + } + } + abstract class BaseConceptProperty { private final String myPropertyName; @@ -165,7 +239,7 @@ public interface IContextValidationSupport } } - class CodeValidationResult { + abstract class CodeValidationResult { private CDCT myDefinition; private String myMessage; private IST mySeverity; @@ -228,6 +302,190 @@ public interface IContextValidationSupport 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 myProperties; + private List myDesignations; + + /** + * Constructor + */ + public LookupCodeResult() { + super(); + } + + public List getProperties() { + if (myProperties == null) { + myProperties = new ArrayList<>(); + } + return myProperties; + } + + public void setProperties(List theProperties) { + myProperties = theProperties; + } + + @Nonnull + public List 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> 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 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); + } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java index 2ea9b162e5e..914278280f6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java @@ -54,15 +54,31 @@ public @interface Operation { String name(); /** - * On a client, this value should be populated with the resource type that the operation applies to. If set to + * This value may be populated with the resource type that the operation applies to. If set to * {@link IBaseResource} (which is the default) than the operation applies to the server and not to a specific * resource type. *

- * This value has no effect when used on server implementations. + * This attribute should not be used a resource provider implementing + * IResourceProvider since the type can be inferred from the + * resource provider type. *

+ * @see #typeName() may also be used to specify a value as a String */ Class 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. + *

+ * This attribute should not be used a resource provider implementing + * IResourceProvider since the type can be inferred from the + * resource provider type. + *

+ * @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 idempotent * (meaning roughly that it does not modify any data or state on the server) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java index 33847cd1ef8..8baefd91050 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java @@ -37,31 +37,30 @@ public class AttachmentUtil { */ @SuppressWarnings("unchecked") public static IPrimitiveType getOrCreateData(FhirContext theContext, ICompositeType theAttachment) { - BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "data"); - List entries = entryChild.getAccessor().getValues(theAttachment); - return entries - .stream() - .map(t -> (IPrimitiveType) t) - .findFirst() - .orElseGet(() -> { - IPrimitiveType binary = newPrimitive(theContext, "base64Binary", null); - entryChild.getMutator().setValue(theAttachment, binary); - return binary; - }); + return getOrCreateChild(theContext, theAttachment, "data", "base64Binary"); } @SuppressWarnings("unchecked") public static IPrimitiveType getOrCreateContentType(FhirContext theContext, ICompositeType theAttachment) { - BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "contentType"); + return getOrCreateChild(theContext, theAttachment, "contentType", "string"); + } + + public static IPrimitiveType getOrCreateUrl(FhirContext theContext, ICompositeType theAttachment) { + return getOrCreateChild(theContext, theAttachment, "url", "uri"); + } + + @SuppressWarnings("unchecked") + private static IPrimitiveType getOrCreateChild(FhirContext theContext, ICompositeType theAttachment, String theChildName, String theChildDatatype) { + BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, theChildName); List entries = entryChild.getAccessor().getValues(theAttachment); return entries .stream() - .map(t -> (IPrimitiveType) t) + .map(t -> (IPrimitiveType) t) .findFirst() .orElseGet(() -> { - IPrimitiveType string = newPrimitive(theContext, "string", null); + IPrimitiveType string = newPrimitive(theContext, theChildDatatype, null); entryChild.getMutator().setValue(theAttachment, string); - return string; + return (IPrimitiveType) string; }); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java index c0a27882b25..f7d616a4147 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java @@ -61,8 +61,8 @@ public class ParametersUtil { List valueValues = valueChild.getAccessor().getValues(nextParameter); valueValues .stream() - .filter(t->t instanceof IPrimitiveType) - .map(t->((IPrimitiveType)t).getValueAsString()) + .filter(t -> t instanceof IPrimitiveType) + .map(t -> ((IPrimitiveType) t).getValueAsString()) .filter(StringUtils::isNotBlank) .forEach(retVal::add); @@ -107,11 +107,11 @@ public class ParametersUtil { /** * Add a paratemer value to a Parameters resource * - * @param theContext The FhirContext - * @param theParameters The Parameters resource - * @param theName The parameter name + * @param theContext The FhirContext + * @param theParameters The Parameters resource + * @param theName The parameter name * @param thePrimitiveDatatype The datatype, e.g. "string", or "uri" - * @param theValue The value + * @param theValue The value */ public static void addParameterToParameters(FhirContext theContext, IBaseParameters theParameters, String theName, String thePrimitiveDatatype, String theValue) { Validate.notBlank(thePrimitiveDatatype, "thePrimitiveDatatype must not be null or empty"); @@ -142,9 +142,99 @@ public class ParametersUtil { return value; } + public static IPrimitiveType createUri(FhirContext theContext, String theValue) { + IPrimitiveType value = (IPrimitiveType) theContext.getElementDefinition("uri").newInstance(theValue); + return value; + } + + public static IPrimitiveType createCode(FhirContext theContext, String theValue) { + IPrimitiveType value = (IPrimitiveType) theContext.getElementDefinition("code").newInstance(theValue); + return value; + } + public static IBaseParameters newInstance(FhirContext theContext) { Validate.notNull(theContext, "theContext must not be null"); return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance(); } + @SuppressWarnings("unchecked") + public static void addParameterToParametersBoolean(FhirContext theCtx, IBaseParameters theParameters, String theName, boolean theValue) { + IPrimitiveType value = (IPrimitiveType) 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 count = (IPrimitiveType) 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 value = (IPrimitiveType) 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 value = (IPrimitiveType) 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 value = (IPrimitiveType) 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 name = (IPrimitiveType) theContext.getElementDefinition("string").newInstance(); + name.setValue(theName); + partChildElem.getChildByName("name").getMutator().addValue(part, name); + + partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue); + } } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java index 86c64ffe0ef..830ade3d8e3 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java @@ -95,6 +95,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { return null; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportR4.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportR4.java index 2e5bbf88aa9..b994fb75985 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportR4.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportR4.java @@ -102,4 +102,9 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java index 7e2d4d472df..ddf0404c509 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java @@ -29,6 +29,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.r4.model.CodeSystem; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -103,7 +104,7 @@ public class UploadTerminologyCommand extends BaseCommand { ourLog.info("Beginning upload - This may take a while..."); IBaseParameters response = client .operation() - .onServer() + .onType(CodeSystem.class) .named(UPLOAD_EXTERNAL_CODE_SYSTEM) .withParameters(inputParameters) .execute(); diff --git a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackValidationSupportDstu3.java b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackValidationSupportDstu3.java index f14b30b4727..6b2a60697e5 100644 --- a/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackValidationSupportDstu3.java +++ b/hapi-fhir-igpacks/src/main/java/ca/uhn/fhir/igpacks/parser/IgPackValidationSupportDstu3.java @@ -127,6 +127,11 @@ public class IgPackValidationSupportDstu3 implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { return null; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index f85968f2d9b..45148a1d9d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -27,6 +27,7 @@ import java.util.*; import javax.annotation.PostConstruct; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.instance.hapi.validation.CachingValidationSupport; @@ -185,13 +186,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 return source; } - private LookupCodeResult lookup(List theContains, String theSystem, String theCode) { + private IContextValidationSupport.LookupCodeResult lookup(List theContains, String theSystem, String theCode) { for (ExpansionContains nextCode : theContains) { String system = nextCode.getSystem(); String code = nextCode.getCode(); if (theSystem.equals(system) && theCode.equals(code)) { - LookupCodeResult retVal = new LookupCodeResult(); + IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult(); retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); retVal.setFound(true); @@ -210,7 +211,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 } @Override - public LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CodingDt theCoding, RequestDetails theRequest) { + public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CodingDt theCoding, RequestDetails theRequest) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -236,13 +237,13 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2 for (IIdType nextId : valueSetIds) { ValueSet expansion = expand(nextId, null, theRequest); List contains = expansion.getExpansion().getContains(); - LookupCodeResult result = lookup(contains, system, code); + IContextValidationSupport.LookupCodeResult result = lookup(contains, system, code); if (result != null) { return result; } } - LookupCodeResult retVal = new LookupCodeResult(); + IContextValidationSupport.LookupCodeResult retVal = new IContextValidationSupport.LookupCodeResult(); retVal.setFound(false); retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java index ed78eb7d175..d01ce0d3c9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java @@ -3,21 +3,15 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.ParametersUtil; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; -import java.util.Collections; +import javax.annotation.Nonnull; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; /* * #%L @@ -43,7 +37,8 @@ public interface IFhirResourceDaoCodeSystem ext List findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest); - LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CD theCoding, RequestDetails theRequestDetails); + @Nonnull + IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CD theCoding, RequestDetails theRequestDetails); SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails); @@ -72,147 +67,4 @@ public interface IFhirResourceDaoCodeSystem 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 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 theProperties) { - myProperties = theProperties; - } - - public void throwNotFoundIfAppropriate() { - if (isFound() == false) { - throw new ResourceNotFoundException("Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]"); - } - } - - public Parameters toParameters(List> 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 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; - } - } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyResourcePid.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyResourcePid.java index c9f7b84d944..40550a3a1cf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyResourcePid.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyResourcePid.java @@ -24,8 +24,9 @@ import org.hl7.fhir.instance.model.api.IAnyResource; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; -final class MetadataKeyResourcePid extends ResourceMetadataKeySupportingAnyResource { +public final class MetadataKeyResourcePid extends ResourceMetadataKeySupportingAnyResource { private static final long serialVersionUID = 1L; MetadataKeyResourcePid(String theValue) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index b183525ff1a..b8acd0d8f0d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -20,25 +20,19 @@ package ca.uhn.fhir.jpa.dao.dstu3; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.jpa.entity.TermConceptDesignation; -import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport.CodeValidationResult; +import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.model.CodeSystem; -import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; @@ -52,45 +46,18 @@ import java.util.Date; import java.util.List; import java.util.Set; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 implements IFhirResourceDaoCodeSystem { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class); - @Autowired - private ITermCodeSystemVersionDao myCsvDao; - @Autowired private ITermCodeSystemDao myCsDao; @Autowired private ValidationSupportChain myValidationSupport; -// private LookupCodeResult lookup(List theContains, String theSystem, String theCode) { -// for (ValueSetExpansionContainsComponent nextCode : theContains) { -// -// String system = nextCode.getSystem(); -// String code = nextCode.getCode(); -// if (theSystem.equals(system) && theCode.equals(code)) { -// LookupCodeResult retVal = new LookupCodeResult(); -// retVal.setSearchedForCode(code); -// retVal.setSearchedForSystem(system); -// retVal.setFound(true); -// if (nextCode.getAbstractElement().getValue() != null) { -// retVal.setCodeIsAbstract(nextCode.getAbstractElement().booleanValue()); -// } -// retVal.setCodeDisplay(nextCode.getDisplay()); -// retVal.setCodeSystemVersion(nextCode.getVersion()); -// retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement -// return retVal; -// } -// -// } -// -// return null; -// } - @Override public List findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { List valueSetIds; @@ -103,7 +70,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -128,40 +95,15 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 toPersistedConcepts(List theConcept, TermCodeSystemVersion theCodeSystemVersion) { - ArrayList retVal = new ArrayList<>(); - - for (ConceptDefinitionComponent next : theConcept) { - if (isNotBlank(next.getCode())) { - TermConcept termConcept = new TermConcept(); - termConcept.setCode(next.getCode()); - termConcept.setCodeSystemVersion(theCodeSystemVersion); - termConcept.setDisplay(next.getDisplay()); - termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); - retVal.add(termConcept); - - for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) { - if (isNotBlank(designationComponent.getValue())) { - TermConceptDesignation designation = termConcept.addDesignation(); - designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null); - if (designationComponent.hasUse()) { - designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null); - designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null); - designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null); - } - designation.setValue(designationComponent.getValue()); - } - } - - // TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here. - } - } - - return retVal; - } - @Override protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); - CodeSystem cs = (CodeSystem) theResource; + CodeSystem csDstu3 = (CodeSystem) theResource; - if (cs != null && isNotBlank(cs.getUrl())) { - String codeSystemUrl = cs.getUrl(); - Long codeSystemResourcePid = retVal.getId(); + org.hl7.fhir.r4.model.CodeSystem cs = VersionConvertor_30_40.convertCodeSystem(csDstu3); + addPidToResource(theEntity, cs); - if (retVal.getDeleted() != null) { - // deleting - } else if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) { - ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString()); - - TermCodeSystemVersion persCs = new TermCodeSystemVersion(); - persCs.setResource(retVal); - persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); - ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); - myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs); - - } - } + myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 2f83a634c66..d5cc4ef233c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.dstu3; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; @@ -238,8 +238,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 } // String code = theCode.getValue(); // String system = toStringOrNull(theSystem); - LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null); - if (result.isFound()) { + IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null); + if (result != null && result.isFound()) { ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay()); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java index f8c5fb944ca..bbcd1a8d95e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java @@ -171,7 +171,7 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap @Override @Transactional(value = TxType.SUPPORTS) public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) { - return fetchCodeSystem(theCtx, theSystem) != null; + return false; } @Override @@ -193,6 +193,11 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3, Ap return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { return null; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 4b6c4d56344..7fc850e4a8b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -20,14 +20,10 @@ package ca.uhn.fhir.jpa.dao.r4; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.jpa.entity.TermConceptDesignation; -import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.LogicUtil; @@ -37,29 +33,25 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.hapi.ctx.IValidationSupport.CodeValidationResult; import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain; import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.IdType; import org.springframework.beans.factory.annotation.Autowired; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; -import static org.apache.commons.lang3.StringUtils.*; +import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 implements IFhirResourceDaoCodeSystem { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR4.class); - @Autowired - private ITermCodeSystemVersionDao myCsvDao; @Autowired private ITermCodeSystemDao myCsDao; @Autowired @@ -76,8 +68,9 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 i return valueSetIds; } + @Nonnull @Override - public LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -104,37 +97,15 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 i if (myValidationSupport.isCodeSystemSupported(getContext(), system)) { ourLog.info("Code system {} is supported", system); - - CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null); - if (result != null) { - if (result.isOk()) { - LookupCodeResult retVal = new LookupCodeResult(); - retVal.setFound(true); - retVal.setSearchedForCode(code); - retVal.setSearchedForSystem(system); - retVal.setCodeDisplay(result.asConceptDefinition().getDisplay()); - - String codeSystemDisplayName = result.getCodeSystemName(); - if (isBlank(codeSystemDisplayName)) { - codeSystemDisplayName = "Unknown"; - } - - retVal.setCodeSystemDisplayName(codeSystemDisplayName); - retVal.setCodeSystemVersion(result.getCodeSystemVersion()); - retVal.setProperties(result.getProperties()); - - return retVal; - } + IContextValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(getContext(), system, code); + if (retVal != null) { + return retVal; } } // We didn't find it.. - LookupCodeResult retVal = new LookupCodeResult(); - retVal.setFound(false); - retVal.setSearchedForCode(code); - retVal.setSearchedForSystem(system); - return retVal; + return IContextValidationSupport.LookupCodeResult.notFound(system, code); } @@ -156,66 +127,15 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 i } } - private List toPersistedConcepts(List theConcept, TermCodeSystemVersion theCodeSystemVersion) { - ArrayList retVal = new ArrayList<>(); - - for (ConceptDefinitionComponent next : theConcept) { - if (isNotBlank(next.getCode())) { - TermConcept termConcept = new TermConcept(); - termConcept.setCode(next.getCode()); - termConcept.setCodeSystemVersion(theCodeSystemVersion); - termConcept.setDisplay(next.getDisplay()); - termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); - retVal.add(termConcept); - - for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) { - if (isNotBlank(designationComponent.getValue())) { - TermConceptDesignation designation = termConcept.addDesignation(); - designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null); - if (designationComponent.hasUse()) { - designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null); - designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null); - designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null); - } - designation.setValue(designationComponent.getValue()); - } - } - - // TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here. - } - } - - return retVal; - } - @Override protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); CodeSystem cs = (CodeSystem) theResource; + addPidToResource(theEntity, theResource); - if (cs != null && isNotBlank(cs.getUrl())) { - String codeSystemUrl = cs.getUrl(); - if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) { - ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString()); - - Long codeSystemResourcePid = retVal.getId(); - TermCodeSystemVersion persCs = myCsvDao.findCurrentVersionForCodeSystemResourcePid(codeSystemResourcePid); - if (persCs != null) { - ourLog.info("Code system version already exists in database"); - } else { - - persCs = new TermCodeSystemVersion(); - persCs.setResource(retVal); - persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); - ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); - myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs); - - } - - } - } + myTerminologySvc.storeNewCodeSystemVersionIfNeeded(cs, theEntity); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index 68e78e778db..4de557a0b34 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.r4; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; @@ -239,7 +239,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple } // String code = theCode.getValue(); // String system = toStringOrNull(theSystem); - LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null); + IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null); if (result.isFound()) { ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay()); return retVal; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/JpaValidationSupportR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/JpaValidationSupportR4.java index f1076beb810..e3352785986 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/JpaValidationSupportR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/JpaValidationSupportR4.java @@ -173,7 +173,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat @Override @Transactional(value = TxType.SUPPORTS) public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) { - return fetchCodeSystem(theCtx, theSystem) != null; + return false; } @Override @@ -195,6 +195,11 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) { return null; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java index 94d4609baa6..2c9ec0bd4ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java @@ -63,6 +63,13 @@ public class TermCodeSystem implements Serializable { @Column(name = "CS_NAME", nullable = true, length = MAX_NAME_LENGTH) private String myName; + /** + * Constructor + */ + public TermCodeSystem() { + super(); + } + public String getCodeSystemUri() { return myCodeSystemUri; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index 969710901ea..e77a935059e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -69,6 +69,9 @@ public class TermCodeSystemVersion implements Serializable { @OneToOne(mappedBy = "myCurrentVersion", optional = true) private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny; + @Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH) + private String myCodeSystemDisplayName; + /** * Constructor */ @@ -155,4 +158,14 @@ public class TermCodeSystemVersion implements Serializable { return result; } + public String getCodeSystemDisplayName() { + return myCodeSystemDisplayName; + } + + public void setCodeSystemDisplayName(String theCodeSystemDisplayName) { + ValidateUtil.isNotTooLongOrThrowIllegalArgument( + theCodeSystemDisplayName, MAX_VERSION_LENGTH, + "Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theCodeSystemDisplayName)); + myCodeSystemDisplayName = theCodeSystemDisplayName; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index 5249be71b4f..8355732d0c8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -111,7 +111,7 @@ public class TermConcept implements Serializable { setCode(theCode); } - public TermConcept addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) { + public TermConceptParentChildLink addChild(TermConcept theChild, RelationshipTypeEnum theRelationshipType) { Validate.notNull(theRelationshipType, "theRelationshipType must not be null"); TermConceptParentChildLink link = new TermConceptParentChildLink(); link.setParent(this); @@ -120,7 +120,7 @@ public class TermConcept implements Serializable { getChildren().add(link); theChild.getParents().add(link); - return this; + return link; } public void addChildren(List theChildren, RelationshipTypeEnum theRelationshipType) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index 144fc1ae632..584d2f15487 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -66,6 +66,14 @@ public class TermConceptProperty implements Serializable { private String myValue; @Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH) private TermConceptPropertyTypeEnum myType; + + /** + * Constructor + */ + public TermConceptProperty() { + super(); + } + /** * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java index cfd022d161c..7266056600e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java @@ -20,12 +20,8 @@ package ca.uhn.fhir.jpa.provider; * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import javax.servlet.http.HttpServletRequest; - +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.jpa.model.util.JpaConstants; @@ -34,27 +30,33 @@ import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.primitive.*; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import javax.servlet.http.HttpServletRequest; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2 { @Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true) public ValueSet expand( - HttpServletRequest theServletRequest, - @IdParam(optional=true) IdDt theId, - @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, - @OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier, - @OperationParam(name = "filter", min=0, max=1) StringDt theFilter, - RequestDetails theRequestDetails) { + HttpServletRequest theServletRequest, + @IdParam(optional = true) IdDt theId, + @OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet, + @OperationParam(name = "identifier", min = 0, max = 1) UriDt theIdentifier, + @OperationParam(name = "filter", min = 0, max = 1) StringDt theFilter, + RequestDetails theRequestDetails) { boolean haveId = theId != null && theId.hasIdPart(); boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue()); boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false; - + if (!haveId && !haveIdentifier && !haveValueSet) { throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request"); } @@ -62,7 +64,7 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) { throw new InvalidRequestException("$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options."); } - + startRequest(theServletRequest); try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); @@ -73,51 +75,35 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst } else { return dao.expand(theValueSet, toFilterString(theFilter)); } - + } finally { endRequest(theServletRequest); } } - - private static boolean moreThanOneTrue(boolean... theBooleans) { - boolean haveOne = false; - for (boolean next : theBooleans) { - if (next) { - if (haveOne) { - return true; - } else { - haveOne = true; - } - } - } - return false; - } - - private String toFilterString(StringDt theFilter) { return theFilter != null ? theFilter.getValue() : null; } - @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { - @OperationParam(name="name", type=StringDt.class, min=1), - @OperationParam(name="version", type=StringDt.class, min=0), - @OperationParam(name="display", type=StringDt.class, min=1), - @OperationParam(name="abstract", type=BooleanDt.class, min=1), + @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = { + @OperationParam(name = "name", type = StringDt.class, min = 1), + @OperationParam(name = "version", type = StringDt.class, min = 0), + @OperationParam(name = "display", type = StringDt.class, min = 1), + @OperationParam(name = "abstract", type = BooleanDt.class, min = 1), }) public Parameters lookup( - HttpServletRequest theServletRequest, - @OperationParam(name="code", min=0, max=1) CodeDt theCode, - @OperationParam(name="system", min=0, max=1) UriDt theSystem, - @OperationParam(name="coding", min=0, max=1) CodingDt theCoding, - RequestDetails theRequestDetails - ) { + HttpServletRequest theServletRequest, + @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode, + @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem, + @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding, + RequestDetails theRequestDetails + ) { startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); - if (result.isFound()==false) { + IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + if (result.isFound() == false) { throw new ResourceNotFoundException("Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]"); } Parameters retVal = new Parameters(); @@ -126,30 +112,29 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst retVal.addParameter().setName("version").setValue(new StringDt(result.getCodeSystemVersion())); } retVal.addParameter().setName("display").setValue(new StringDt(result.getCodeDisplay())); - retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract())); + retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract())); return retVal; } finally { endRequest(theServletRequest); } } - - - @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters= { - @OperationParam(name="result", type=BooleanDt.class, min=1), - @OperationParam(name="message", type=StringDt.class), - @OperationParam(name="display", type=StringDt.class) + + @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = { + @OperationParam(name = "result", type = BooleanDt.class, min = 1), + @OperationParam(name = "message", type = StringDt.class), + @OperationParam(name = "display", type = StringDt.class) }) public Parameters validateCode( - HttpServletRequest theServletRequest, - @IdParam(optional=true) IdDt theId, - @OperationParam(name="identifier", min=0, max=1) UriDt theValueSetIdentifier, - @OperationParam(name="code", min=0, max=1) CodeDt theCode, - @OperationParam(name="system", min=0, max=1) UriDt theSystem, - @OperationParam(name="display", min=0, max=1) StringDt theDisplay, - @OperationParam(name="coding", min=0, max=1) CodingDt theCoding, - @OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept, - RequestDetails theRequestDetails - ) { + HttpServletRequest theServletRequest, + @IdParam(optional = true) IdDt theId, + @OperationParam(name = "identifier", min = 0, max = 1) UriDt theValueSetIdentifier, + @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode, + @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem, + @OperationParam(name = "display", min = 0, max = 1) StringDt theDisplay, + @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding, + @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConceptDt theCodeableConcept, + RequestDetails theRequestDetails + ) { startRequest(theServletRequest); try { @@ -169,5 +154,19 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst } } - + private static boolean moreThanOneTrue(boolean... theBooleans) { + boolean haveOne = false; + for (boolean next : theBooleans) { + if (next) { + if (haveOne) { + return true; + } else { + haveOne = true; + } + } + } + return false; + } + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java index 0ee3b80dbfb..319ea0a97f3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java @@ -1,142 +1,19 @@ package ca.uhn.fhir.jpa.provider; -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2019 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% +import ca.uhn.fhir.jpa.model.util.JpaConstants; + +/** + * @deprecated TerminologyUploaderProvider */ +@Deprecated +public class BaseTerminologyUploaderProvider { -import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; -import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import org.hl7.fhir.r4.model.*; -import org.springframework.beans.factory.annotation.Autowired; - -import javax.servlet.http.HttpServletRequest; -import java.io.*; -import java.util.ArrayList; -import java.util.List; - -import static org.apache.commons.lang3.StringUtils.*; - -public abstract class BaseTerminologyUploaderProvider extends BaseJpaProvider { - public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTerminologyUploaderProvider.class); + // FIXME: remove these before 4.0.0 + public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM; public static final String CONCEPT_COUNT = "conceptCount"; public static final String TARGET = "target"; - - @Autowired - private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; - - protected Parameters handleUploadExternalCodeSystem( - HttpServletRequest theServletRequest, - StringParam theCodeSystemUrl, - List theLocalFile, - List 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 localFiles = new ArrayList<>(); - if (theLocalFile != null && theLocalFile.size() > 0) { - for (StringType nextLocalFile : theLocalFile) { - if (isNotBlank(nextLocalFile.getValue())) { - ourLog.info("Reading in local file: {}", nextLocalFile.getValue()); - File nextFile = new File(nextLocalFile.getValue()); - if (!nextFile.exists() || !nextFile.isFile()) { - throw new InvalidRequestException("Unknown file: " + nextFile.getName()); - } - localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { - @Override - public String getFilename() { - return nextFile.getAbsolutePath(); - } - - @Override - public InputStream getInputStream() { - try { - return new FileInputStream(nextFile); - } catch (FileNotFoundException theE) { - throw new InternalErrorException(theE); - } - } - }); - } - } - } - - if (thePackage != null) { - for (Attachment nextPackage : thePackage) { - if (isBlank(nextPackage.getUrl())) { - throw new UnprocessableEntityException("Package is missing mandatory url element"); - } - - localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { - @Override - public String getFilename() { - return nextPackage.getUrl(); - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(nextPackage.getData()); - } - }); - } - } - - String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null; - url = defaultString(url); - - UploadStatistics stats; - switch(url) { - case IHapiTerminologyLoaderSvc.SCT_URI: - stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails); - break; - case IHapiTerminologyLoaderSvc.LOINC_URI: - stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails); - break; - case IHapiTerminologyLoaderSvc.IMGTHLA_URI: - stats = myTerminologyLoaderSvc.loadImgthla(localFiles, theRequestDetails); - break; - default: - throw new InvalidRequestException("Unknown URL: " + url); - } - - Parameters retVal = new Parameters(); - retVal.addParameter().setName(CONCEPT_COUNT).setValue(new IntegerType(stats.getConceptCount())); - retVal.addParameter().setName(TARGET).setValue(new Reference(stats.getTarget().getValue())); - return retVal; - } finally { - endRequest(theServletRequest); - } - } - + public static final String SYSTEM = "system"; + public static final String PARENT_CODE = "parentCode"; + public static final String VALUE = "value"; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java new file mode 100644 index 00000000000..7ea58523dd6 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -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; + + /** + * + * $apply-codesystem-delta-add + * + */ + @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 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); + } + + } + + + /** + * + * $apply-codesystem-delta-remove + * + */ + @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); + } + + } + + + /** + * + * $upload-external-codesystem + * + */ + @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 theCodeSystemUrl, + @OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED, typeName = "string") List> theLocalFile, + @OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List 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 localFiles = new ArrayList<>(); + if (theLocalFile != null && theLocalFile.size() > 0) { + for (IPrimitiveType 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); + } + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 8e1546f1bb4..999a3ab677b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -20,14 +20,13 @@ package ca.uhn.fhir.jpa.provider.dstu3; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.exceptions.FHIRException; @@ -36,29 +35,27 @@ import java.util.List; public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderDstu3 { - @SuppressWarnings("unchecked") - @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { - @OperationParam(name="name", type=StringType.class, min=1), - @OperationParam(name="version", type=StringType.class, min=0), - @OperationParam(name="display", type=StringType.class, min=1), - @OperationParam(name="abstract", type=BooleanType.class, min=1), + @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = { + @OperationParam(name = "name", type = StringType.class, min = 1), + @OperationParam(name = "version", type = StringType.class, min = 0), + @OperationParam(name = "display", type = StringType.class, min = 1), + @OperationParam(name = "abstract", type = BooleanType.class, min = 1), }) public Parameters lookup( - HttpServletRequest theServletRequest, - @OperationParam(name="code", min=0, max=1) CodeType theCode, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="coding", min=0, max=1) Coding theCoding, - @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, - RequestDetails theRequestDetails - ) { + HttpServletRequest theServletRequest, + @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, + @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, + @OperationParam(name = "coding", min = 0, max = 1) Coding theCoding, + @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, + RequestDetails theRequestDetails + ) { startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); result.throwNotFoundIfAppropriate(); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(theProperties); - return VersionConvertor_30_40.convertParameters(parametersR4); + return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } catch (FHIRException e) { throw new InternalErrorException(e); } finally { @@ -70,16 +67,16 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD /** * $subsumes operation */ - @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters= { - @OperationParam(name="outcome", type= CodeType.class, min=1), + @Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters = { + @OperationParam(name = "outcome", type = CodeType.class, min = 1), }) public Parameters subsumes( HttpServletRequest theServletRequest, - @OperationParam(name="codeA", min=0, max=1) CodeType theCodeA, - @OperationParam(name="codeB", min=0, max=1) CodeType theCodeB, - @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="codingA", min=0, max=1) Coding theCodingA, - @OperationParam(name="codingB", min=0, max=1) Coding theCodingB, + @OperationParam(name = "codeA", min = 0, max = 1) CodeType theCodeA, + @OperationParam(name = "codeB", min = 0, max = 1) CodeType theCodeB, + @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, + @OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA, + @OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB, RequestDetails theRequestDetails ) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java index 3d764edc326..0ede86b447b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java @@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.dstu3; * #L% */ -import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -37,37 +38,10 @@ import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; -public class TerminologyUploaderProviderDstu3 extends BaseTerminologyUploaderProvider { - - @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { - @OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) - }) - public Parameters uploadExternalCodeSystem( - HttpServletRequest theServletRequest, - @OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl, - @OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List theLocalFile, - @OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List thePackage, - RequestDetails theRequestDetails - ) { - try { - List localFile = null; - if (theLocalFile != null) { - localFile = new ArrayList<>(); - for (StringType next : theLocalFile) { - localFile.add(VersionConvertor_30_40.convertString(next)); - } - } - List pkg = null; - if (thePackage!=null){ - pkg = new ArrayList<>(); - for (Attachment next : thePackage) { - pkg.add(VersionConvertor_30_40.convertAttachment(next)); - } - } - org.hl7.fhir.r4.model.Parameters retValR4 = handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, localFile, pkg, theRequestDetails); - return VersionConvertor_30_40.convertParameters(retValR4); - } catch (FHIRException e) { - throw new InternalErrorException(e); - } - } +/** + * @deprecated Use {@link TerminologyUploaderProvider} instead + */ +@Deprecated +public class TerminologyUploaderProviderDstu3 extends TerminologyUploaderProvider { + // nothing } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 8c7f4e93732..3ded90474c8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4; * #L% */ +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -55,9 +55,9 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); - LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); result.throwNotFoundIfAppropriate(); - return result.toParameters(theProperties); + return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java index 93ed559e946..06b699a22c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java @@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.provider.r4; * #L% */ -import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -33,18 +34,9 @@ import org.hl7.fhir.r4.model.StringType; import javax.servlet.http.HttpServletRequest; import java.util.List; -public class TerminologyUploaderProviderR4 extends BaseTerminologyUploaderProvider { - - @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { - @OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) - }) - public Parameters uploadExternalCodeSystem( - HttpServletRequest theServletRequest, - @OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl, - @OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List theLocalFile, - @OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List thePackage, - RequestDetails theRequestDetails - ) { - return handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, theLocalFile, thePackage, theRequestDetails); - } +/** + * @deprecated Use {@link TerminologyUploaderProvider} instead + */ +public class TerminologyUploaderProviderR4 extends TerminologyUploaderProvider { + // nothing } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 3119b7350af..a3cbf53253d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -21,10 +21,8 @@ package ca.uhn.fhir.jpa.term; */ import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; +import ca.uhn.fhir.context.support.IContextValidationSupport; +import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; @@ -78,6 +76,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; @@ -261,6 +260,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource); + protected void validateCodeSystemForStorage(CodeSystem theCodeSystemResource) { + ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theCodeSystemResource.getUrl(), "Can not store a CodeSystem without a valid URL"); + } + protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap); abstract void createOrUpdateValueSet(ValueSet theValueSet); @@ -767,9 +770,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, @Override public Optional findCode(String theCodeSystem, String theCode) { - TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem); - - return myConceptDao.findByCodeSystemAndCode(csv, theCode); + /* + * Loading concepts without a transaction causes issues later on some + * platforms (e.g. PSQL) so this transactiontemplate is here to make + * sure that we always call this with an open transaction + */ + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY); + return txTemplate.execute(t->{ + TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem); + return myConceptDao.findByCodeSystemAndCode(csv, theCode); + }); } @Override @@ -1036,6 +1047,37 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } + /** + * Returns the number of saved concepts + */ + private int saveOrUpdateConcept(TermConcept theConcept) { + + TermCodeSystemVersion csv = theConcept.getCodeSystemVersion(); + Optional existing = myConceptDao.findByCodeSystemAndCode(csv, theConcept.getCode()); + if (existing.isPresent()) { + TermConcept existingConcept = existing.get(); + boolean haveChanges = false; + if (!StringUtils.equals(existingConcept.getDisplay(), theConcept.getDisplay())) { + existingConcept.setDisplay(theConcept.getDisplay()); + haveChanges = true; + } + + if (!haveChanges) { + return 0; + } + + myConceptDao.save(existingConcept); + return 1; + + } else { + return saveConcept(theConcept); + } + + } + + /** + * Returns the number of saved concepts + */ private int saveConcept(TermConcept theConcept) { int retVal = 0; @@ -1124,7 +1166,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, @Override @Transactional(propagation = Propagation.REQUIRED) - public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSystemVersion) { + public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion) { ourLog.info("Storing code system"); ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied"); @@ -1170,6 +1212,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } theCodeSystemVersion.setCodeSystem(codeSystem); + theCodeSystemVersion.setCodeSystemDisplayName(theSystemName); + theCodeSystemVersion.setCodeSystemVersionId(theSystemVersionId); + ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)"); // Validate the code system @@ -1226,8 +1271,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, ourLog.info("CodeSystem resource has ID: {}", csId.getValue()); - theCodeSystemVersion.setResource(resource); - storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion); + populateCodeSystemVersionProperties(theCodeSystemVersion, theCodeSystemResource, resource); + + storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemResource.getVersion(), theCodeSystemVersion); myDeferredConceptMaps.addAll(theConceptMaps); myDeferredValueSets.addAll(theValueSets); @@ -1235,6 +1281,99 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return csId; } + private void populateCodeSystemVersionProperties(TermCodeSystemVersion theCodeSystemVersion, CodeSystem theCodeSystemResource, ResourceTable theResourceTable) { + theCodeSystemVersion.setResource(theResourceTable); + theCodeSystemVersion.setCodeSystemDisplayName(theCodeSystemResource.getName()); + theCodeSystemVersion.setCodeSystemVersionId(theCodeSystemResource.getVersion()); + } + + @Override + public void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity) { + if (theCodeSystem != null && isNotBlank(theCodeSystem.getUrl())) { + String codeSystemUrl = theCodeSystem.getUrl(); + if (theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.COMPLETE || theCodeSystem.getContent() == null || theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) { + ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", theResourceEntity.getIdDt().getValue(), theCodeSystem.getContentElement().getValueAsString()); + + Long codeSystemResourcePid = IDao.RESOURCE_PID.get(theCodeSystem); + TermCodeSystemVersion persCs = myCodeSystemVersionDao.findCurrentVersionForCodeSystemResourcePid(codeSystemResourcePid); + if (persCs != null) { + ourLog.info("Code system version already exists in database"); + } else { + + persCs = new TermCodeSystemVersion(); + populateCodeSystemVersionProperties(persCs, theCodeSystem, theResourceEntity); + + persCs.getConcepts().addAll(toPersistedConcepts(theCodeSystem.getConcept(), persCs)); + ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); + storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, theCodeSystem.getName(), theCodeSystem.getVersion(), persCs); + + } + + } + } + } + + private List toPersistedConcepts(List theConcept, TermCodeSystemVersion theCodeSystemVersion) { + ArrayList retVal = new ArrayList<>(); + + for (CodeSystem.ConceptDefinitionComponent next : theConcept) { + if (isNotBlank(next.getCode())) { + TermConcept termConcept = toTermConcept(next, theCodeSystemVersion); + retVal.add(termConcept); + } + } + + return retVal; + } + + @Nonnull + private TermConcept toTermConcept(CodeSystem.ConceptDefinitionComponent theConceptDefinition, TermCodeSystemVersion theCodeSystemVersion) { + TermConcept termConcept = new TermConcept(); + termConcept.setCode(theConceptDefinition.getCode()); + termConcept.setCodeSystemVersion(theCodeSystemVersion); + termConcept.setDisplay(theConceptDefinition.getDisplay()); + termConcept.addChildren(toPersistedConcepts(theConceptDefinition.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); + + for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : theConceptDefinition.getDesignation()) { + if (isNotBlank(designationComponent.getValue())) { + TermConceptDesignation designation = termConcept.addDesignation(); + designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null); + if (designationComponent.hasUse()) { + designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null); + designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null); + designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null); + } + designation.setValue(designationComponent.getValue()); + } + } + + for (CodeSystem.ConceptPropertyComponent next : theConceptDefinition.getProperty()) { + TermConceptProperty property = new TermConceptProperty(); + + property.setKey(next.getCode()); + property.setConcept(termConcept); + property.setCodeSystemVersion(theCodeSystemVersion); + + if (next.getValue() instanceof StringType) { + property.setType(TermConceptPropertyTypeEnum.STRING); + property.setValue(next.getValueStringType().getValue()); + } else if (next.getValue() instanceof Coding) { + Coding nextCoding = next.getValueCoding(); + property.setType(TermConceptPropertyTypeEnum.CODING); + property.setCodeSystem(nextCoding.getSystem()); + property.setValue(nextCoding.getCode()); + property.setDisplay(nextCoding.getDisplay()); + } else if (next.getValue() != null) { + // TODO: LOINC has properties of type BOOLEAN that we should handle + ourLog.warn("Don't know how to handle properties of type: " + next.getValue().getClass()); + continue; + } + + termConcept.getProperties().add(property); + } + return termConcept; + } + @Override @Transactional public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) { @@ -1443,6 +1582,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } @Override + @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); @@ -1471,6 +1611,136 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes); } + @Transactional + @Override + public AtomicInteger applyDeltaCodesystemsAdd(String theSystem, @Nullable String theParent, CodeSystem theValue) { + TermCodeSystem cs = getCodeSystem(theSystem); + if (cs == null) { + List 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 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 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 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 codeOpt = findCode(theSystem, theCode); + if (codeOpt.isPresent()) { + TermConcept code = codeOpt.get(); + + IContextValidationSupport.LookupCodeResult result = new IContextValidationSupport.LookupCodeResult(); + result.setCodeSystemDisplayName(code.getCodeSystemVersion().getCodeSystemDisplayName()); + result.setCodeSystemVersion(code.getCodeSystemVersion().getCodeSystemVersionId()); + result.setSearchedForSystem(theSystem); + result.setSearchedForCode(theCode); + result.setFound(true); + result.setCodeDisplay(code.getDisplay()); + + for (TermConceptDesignation next : code.getDesignations()) { + IContextValidationSupport.ConceptDesignation designation = new IContextValidationSupport.ConceptDesignation(); + designation.setLanguage(next.getLanguage()); + designation.setUseSystem(next.getUseSystem()); + designation.setUseCode(next.getUseCode()); + designation.setUseDisplay(next.getUseDisplay()); + designation.setValue(next.getValue()); + result.getDesignations().add(designation); + } + + for (TermConceptProperty next : code.getProperties()) { + if (next.getType() == TermConceptPropertyTypeEnum.CODING) { + IContextValidationSupport.CodingConceptProperty property = new IContextValidationSupport.CodingConceptProperty(next.getKey(), next.getCodeSystem(), next.getValue(), next.getDisplay()); + result.getProperties().add(property); + } else if (next.getType() == TermConceptPropertyTypeEnum.STRING) { + IContextValidationSupport.StringConceptProperty property = new IContextValidationSupport.StringConceptProperty(next.getKey(), next.getValue()); + result.getProperties().add(property); + } else { + throw new InternalErrorException("Unknown type: " + next.getType()); + } + } + + return result; + + } else { + return null; + } + }); + } + private @Nullable ConceptSubsumptionOutcome testForSubsumption(FullTextEntityManager theEntityManager, TermConcept theLeft, TermConcept theRight, ConceptSubsumptionOutcome theOutput) { QueryBuilder qb = theEntityManager.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get(); @@ -1608,7 +1878,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, predicates = new ArrayList<>(); coding = translationQuery.getCoding(); - String targetCode = null; + String targetCode; String targetCodeSystem = null; if (coding.hasCode()) { predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); @@ -1716,12 +1986,10 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return retVal; } - private void verifyNoDuplicates(Collection theConcepts, Set theCodes) { - for (TermConcept next : theConcepts) { - if (!theCodes.add(next.getCode())) { - throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes"); - } - verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes); + private static void extractLinksFromConceptAndChildren(TermConcept theConcept, List theLinks) { + theLinks.addAll(theConcept.getParents()); + for (TermConceptParentChildLink child : theConcept.getChildren()) { + extractLinksFromConceptAndChildren(child.getChild(), theLinks); } } @@ -1775,4 +2043,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) { ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest; } + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index ecd38e79851..48f5cd600a0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -19,6 +19,9 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Collections; @@ -61,6 +64,8 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen private IValidationSupport myValidationSupport; @Autowired private IHapiTerminologySvc myTerminologySvc; + @Autowired + private PlatformTransactionManager myTransactionManager; /** * Constructor @@ -100,6 +105,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen } catch (FHIRException e) { throw new InternalErrorException(e); } + validateCodeSystemForStorage(theCodeSystemResource); if (isBlank(resourceToStore.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId(); @@ -217,14 +223,14 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen return null; } - @CoverageIgnore @Override - public ValueSet fetchValueSet(FhirContext theContext, String theSystem) { + public IBaseResource fetchResource(FhirContext theContext, Class theClass, String theUri) { return null; } + @CoverageIgnore @Override - public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + public ValueSet fetchValueSet(FhirContext theContext, String theSystem) { return null; } @@ -293,20 +299,30 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen @CoverageIgnore @Override - public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { - Optional codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode); - if (codeOpt.isPresent()) { - ConceptDefinitionComponent def = new ConceptDefinitionComponent(); - TermConcept code = codeOpt.get(); - def.setCode(code.getCode()); - def.setDisplay(code.getDisplay()); - CodeValidationResult retVal = new CodeValidationResult(def); - retVal.setProperties(code.toValidationProperties()); - retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName()); - return retVal; - } + public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + return txTemplate.execute(t->{ + Optional codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode); + if (codeOpt.isPresent()) { + ConceptDefinitionComponent def = new ConceptDefinitionComponent(); + TermConcept code = codeOpt.get(); + def.setCode(code.getCode()); + def.setDisplay(code.getDisplay()); + IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def); + retVal.setProperties(code.toValidationProperties()); + retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName()); + return retVal; + } - return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); + return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); + }); + + } + + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return super.lookupCode(theContext, theSystem, theCode); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index a8b849424a0..1e1bbe84b96 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.UrlUtil; +import ca.uhn.fhir.util.ValidateUtil; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; @@ -18,6 +19,9 @@ import org.hl7.fhir.r4.terminologies.ValueSetExpander; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.Collections; @@ -48,6 +52,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; */ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements IHapiTerminologySvcR4 { + @Autowired @Qualifier("myConceptMapDaoR4") private IFhirResourceDao myConceptMapResourceDao; @@ -60,7 +65,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements @Autowired private IValidationSupport myValidationSupport; @Autowired - private IHapiTerminologySvc myTerminologySvc; + private PlatformTransactionManager myTransactionManager; private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List theListToPopulate) { if (isNotBlank(theCode.getCode())) { @@ -87,6 +92,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements @Override protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { + validateCodeSystemForStorage(theCodeSystemResource); if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId(); @@ -227,7 +233,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements @Override public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { - return myTerminologySvc.supportsSystem(theSystem); + return supportsSystem(theSystem); } @Override @@ -237,19 +243,28 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements @CoverageIgnore @Override - public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { - Optional codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode); - if (codeOpt.isPresent()) { - TermConcept code = codeOpt.get(); - ConceptDefinitionComponent def = new ConceptDefinitionComponent(); - def.setCode(code.getCode()); - def.setDisplay(code.getDisplay()); - CodeValidationResult retVal = new CodeValidationResult(def); - retVal.setProperties(code.toValidationProperties()); - return retVal; - } + public IValidationSupport.CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + return txTemplate.execute(t-> { + Optional codeOpt = findCode(theCodeSystem, theCode); + if (codeOpt.isPresent()) { + TermConcept code = codeOpt.get(); + ConceptDefinitionComponent def = new ConceptDefinitionComponent(); + def.setCode(code.getCode()); + def.setDisplay(code.getDisplay()); + IValidationSupport.CodeValidationResult retVal = new IValidationSupport.CodeValidationResult(def); + retVal.setProperties(code.toValidationProperties()); + return retVal; + } - return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); + return new IValidationSupport.CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); + }); + } + + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return super.lookupCode(theContext, theSystem, theCode); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java index ffd0540ef4b..61d7151e1b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java @@ -8,12 +8,15 @@ import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; +import javax.annotation.Nullable; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /* * #%L @@ -76,13 +79,15 @@ public interface IHapiTerminologySvc { */ void setProcessDeferred(boolean theProcessDeferred); - void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSytemVersion); + void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion); /** * @return Returns the ID of the created/updated code system */ IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List theValueSets, List theConceptMaps); + void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity); + void deleteConceptMapAndChildren(ResourceTable theResourceTable); void deleteValueSetAndChildren(ResourceTable theResourceTable); @@ -98,4 +103,8 @@ public interface IHapiTerminologySvc { List translateWithReverse(TranslationRequest theTranslationRequest); IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB); + + AtomicInteger applyDeltaCodesystemsAdd(String theSystem, @Nullable String theParent, CodeSystem theValue); + + AtomicInteger applyDeltaCodesystemsRemove(String theSystem, CodeSystem theDelta); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 64390e9a671..4fb3c04d103 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; @@ -63,6 +63,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + codeSystem.setName("ACME Codes"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); @@ -98,7 +99,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); parentC.addChild(childCA, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs); return codeSystem; } @@ -129,7 +130,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { parentB.addChild(childI, RelationshipTypeEnum.ISA); } - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs); return codeSystem; } @@ -165,7 +166,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); dogs.addChild(beagle, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME", "SYSTEM VERSION" , cs); return codeSystem; } @@ -489,14 +490,18 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { createExternalCsAndLocalVs(); + // We're making sure that a reindex doesn't wipe out all of the + // stored codes in the terminology service myResourceReindexingSvc.markAllResourcesForReindexing(); myResourceReindexingSvc.forceReindexingPass(); myResourceReindexingSvc.forceReindexingPass(); + myHapiTerminologySvc.saveDeferred(); + myHapiTerminologySvc.saveDeferred(); + myHapiTerminologySvc.saveDeferred(); + + IContextValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd); + assertEquals(true, lookupResults.isFound()); - myHapiTerminologySvc.saveDeferred(); - myHapiTerminologySvc.saveDeferred(); - myHapiTerminologySvc.saveDeferred(); - ValueSet vs = new ValueSet(); ConceptSetComponent include = vs.getCompose().addInclude(); include.setSystem(URL_MY_CODE_SYSTEM); @@ -680,11 +685,11 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { cs.setResource(table); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", "SYSTEM VERSION" , cs); StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); + IContextValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 25ecf91a662..edb8701dab9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -22,6 +22,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry; import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry; import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.util.ResourceProviderFactory; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4; @@ -127,6 +128,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Autowired protected ITermCodeSystemDao myTermCodeSystemDao; @Autowired + protected ITermConceptParentChildLinkDao myTermConceptParentChildLinkDao; + @Autowired protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao; @Autowired @Qualifier("myCompartmentDefinitionDaoR4") @@ -288,7 +291,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Qualifier("myTaskDaoR4") protected IFhirResourceDao myTaskDao; @Autowired - protected IHapiTerminologySvc myTermSvc; + protected IHapiTerminologySvcR4 myTermSvc; @Autowired protected PlatformTransactionManager myTransactionMgr; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 7cdb50077d2..24ba6e09e0e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; @@ -96,7 +96,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); parentC.addChild(childCA, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs); return codeSystem; } @@ -132,7 +132,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); dogs.addChild(beagle, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs); return codeSystem; } @@ -163,7 +163,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { parentB.addChild(childI, RelationshipTypeEnum.ISA); } - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs); return codeSystem; } @@ -476,7 +476,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { concept = new TermConcept(cs, "LA9999-7"); cs.getConcepts().add(concept); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs); ValueSet valueSet = new ValueSet(); valueSet.setUrl(URL_MY_VALUE_SET); @@ -803,11 +803,11 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { cs.setResource(table); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", "SYSTEM VERSION" , cs); StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); + IContextValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index 2b3e03ee5fc..b26937a2f03 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -55,10 +55,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("SYSTEM VERSION"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); // With HTTP GET respParam = ourClient @@ -75,10 +77,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("SYSTEM VERSION", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } @@ -96,11 +100,13 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(("v2 Identifier Type"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("2.8.2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } @Test @@ -123,7 +129,6 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst @Test public void testLookupOperationByCodeAndSystemUserDefinedCode() { - //@formatter:off Parameters respParam = ourClient .operation() .onType(CodeSystem.class) @@ -131,13 +136,12 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst .withParameter(Parameters.class, "code", new CodeType("8450-9")) .andParameter("system", new UriType("http://acme.org")) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("ACME Codes", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -164,20 +168,18 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst @Test public void testLookupOperationByCoding() { - //@formatter:off Parameters respParam = ourClient .operation() .onType(CodeSystem.class) .named("lookup") .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -239,9 +241,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst } @Test -// @Ignore public void testLookupOperationForBuiltInCode() { - //@formatter:off Parameters respParam = ourClient .operation() .onType(CodeSystem.class) @@ -249,17 +249,18 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst .withParameter(Parameters.class, "code", new CodeType("M")) .andParameter("system", new UriType("http://hl7.org/fhir/v3/MaritalStatus")) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue()); + assertEquals("v3 Code System MaritalStatus", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2016-11-11", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Married", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue()); } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 3b0b4a91101..af7fcd14fac 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -126,7 +126,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 code.addPropertyString("HELLO", "12345-2"); cs.getConcepts().add(code); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs); }); } @@ -233,7 +233,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 @Test - public void testExpandById() throws IOException { + public void testExpandById() { //@formatter:off Parameters respParam = ourClient .operation() @@ -263,7 +263,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandByIdWithFilter() throws IOException { + public void testExpandByIdWithFilter() { //@formatter:off Parameters respParam = ourClient @@ -350,7 +350,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandInlineVsAgainstBuiltInCs() throws IOException { + public void testExpandInlineVsAgainstBuiltInCs() { createLocalVsPointingAtBuiltInCodeSystem(); assertNotNull(myLocalValueSetId); @@ -371,7 +371,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandInlineVsAgainstExternalCs() throws IOException { + public void testExpandInlineVsAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalVs); myLocalVs.setId(""); @@ -446,7 +446,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandLocalVsAgainstBuiltInCs() throws IOException { + public void testExpandLocalVsAgainstBuiltInCs() { createLocalVsPointingAtBuiltInCodeSystem(); assertNotNull(myLocalValueSetId); @@ -467,7 +467,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandLocalVsAgainstExternalCs() throws IOException { + public void testExpandLocalVsAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalValueSetId); @@ -491,7 +491,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException { + public void testExpandLocalVsCanonicalAgainstExternalCs() { createExternalCsAndLocalVs(); assertNotNull(myLocalValueSetId); @@ -515,7 +515,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } @Test - public void testExpandLocalVsWithUnknownCode() throws IOException { + public void testExpandLocalVsWithUnknownCode() { createExternalCsAndLocalVsWithUnknownCode(); assertNotNull(myLocalValueSetId); @@ -636,7 +636,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 ourLog.info(resp); assertEquals("result", respParam.getParameter().get(0).getName()); - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue()); + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("message", respParam.getParameter().get(1).getName()); assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), Matchers.containsStringIgnoringCase("succeeded")); @@ -664,7 +664,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 ourLog.info(resp); assertEquals("result", respParam.getParameter().get(0).getName()); - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue()); + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("message", respParam.getParameter().get(1).getName()); assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), Matchers.containsStringIgnoringCase("succeeded")); @@ -682,6 +682,8 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + codeSystem.setName("ACME Codes"); + codeSystem.setVersion("1.2.3.4"); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); @@ -707,7 +709,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); cs.getConcepts().add(parentB); - theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs); return codeSystem; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java index 90bb75cc09f..b792a29aa06 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java @@ -85,7 +85,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) .andParameter("package", new Attachment().setUrl("foo").setData(packageBytes)) @@ -102,7 +102,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) @@ -111,7 +111,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); /* * Try uploading a second time @@ -119,7 +119,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) @@ -136,7 +136,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setData(packageBytes)) @@ -154,7 +154,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "package", new Attachment().setUrl("foo").setData(packageBytes)) .execute(); @@ -170,7 +170,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .execute(); @@ -186,7 +186,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) @@ -195,7 +195,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); } @Test @@ -212,7 +212,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) @@ -221,7 +221,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index dbd30d04d4b..db84dfa5787 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.TestUtil; @@ -14,8 +16,8 @@ import org.springframework.transaction.annotation.Transactional; import java.io.IOException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.*; public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test { @@ -45,6 +47,93 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test } + @Test + public void testApplyDeltaAdd() { + + CodeSystem delta = new CodeSystem(); + delta.setUrl("http://example.com/labCodes"); + delta.setName("Example Hospital Lab Codes"); + delta.setStatus(Enumerations.PublicationStatus.ACTIVE); + delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + delta.setUrl("http://foo"); + CodeSystem.ConceptDefinitionComponent chem = delta + .addConcept() + .setCode("CHEM") + .setDisplay("Chemistry Tests"); + chem + .addConcept() + .setCode("HB") + .setDisplay("Hemoglobin"); + chem + .addConcept() + .setCode("NEUT") + .setDisplay("Neutrophil"); + CodeSystem.ConceptDefinitionComponent micro = delta + .addConcept() + .setCode("MICRO") + .setDisplay("Microbiology Tests"); + micro + .addConcept() + .setCode("C&S") + .setDisplay("Culture & Sensitivity"); + + LoggingInterceptor interceptor = new LoggingInterceptor(true); + ourClient.registerInterceptor(interceptor); + Parameters outcome = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) + .withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta) + .prettyPrint() + .execute(); + ourClient.unregisterInterceptor(interceptor); + + String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); + ourLog.info(encoded); + assertThat(encoded, containsString("\"valueInteger\": 5")); + } + + @Test + public void testApplyDeltaRemove() { + // Create not-present + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://foo"); + cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + ourClient.create().resource(cs).execute(); + + CodeSystem delta = new CodeSystem(); + delta.setUrl("http://foo"); + delta + .addConcept() + .setCode("codeA") + .setDisplay("displayA"); + + // Add + ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) + .withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta) + .prettyPrint() + .execute(); + + // Remove + LoggingInterceptor interceptor = new LoggingInterceptor(true); + ourClient.registerInterceptor(interceptor); + Parameters outcome = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE) + .withParameter(Parameters.class, TerminologyUploaderProvider.VALUE, delta) + .prettyPrint() + .execute(); + ourClient.unregisterInterceptor(interceptor); + + String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); + ourLog.info(encoded); + assertThat(encoded, containsString("\"valueInteger\": 1")); + } + @Test public void testLookupOnExternalCode() { ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd); @@ -61,11 +150,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("SYSTEM VERSION", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); // With HTTP GET respParam = ourClient @@ -81,11 +172,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("SYSTEM VERSION"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } @@ -103,11 +196,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } @Test @@ -140,7 +235,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -176,7 +271,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -245,11 +340,13 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue()); + assertEquals("v3.MaritalStatus", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2018-08-12", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Married", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue()); } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index c334dac18cd..a6e1011f511 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -16,7 +16,6 @@ import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.hl7.fhir.instance.model.api.IIdType; @@ -514,7 +513,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info(resp); assertEquals("result", respParam.getParameter().get(0).getName()); - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue()); + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("message", respParam.getParameter().get(1).getName()); assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded")); @@ -603,7 +602,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); cs.getConcepts().add(parentB); - theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); + theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs); return codeSystem; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index b271b487457..e97cd9866a5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -49,7 +49,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) @@ -65,35 +65,31 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes public void testUploadLoinc() throws Exception { byte[] packageBytes = TerminologyUploaderProviderDstu3Test.createLoincZip(); - //@formatter:off Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); - assertThat(((Reference) respParam.getParameter().get(1).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9]+")); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); + assertThat(((Reference) respParam.getParameter().get(2).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9]+")); /* * Try uploading a second time */ - //@formatter:off respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); - //@formatter:on resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); @@ -106,7 +102,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .execute(); @@ -114,18 +110,16 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage()); } - //@formatter:on } @Test public void testUploadMissingUrl() throws Exception { byte[] packageBytes = createSctZip(); - //@formatter:off try { ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); @@ -133,27 +127,25 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage()); } - //@formatter:on + } @Test public void testUploadSct() throws Exception { byte[] packageBytes = createSctZip(); - //@formatter:off Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); } @Test @@ -166,20 +158,18 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes fos.write(packageBytes); fos.close(); - //@formatter:off Parameters respParam = ourClient .operation() - .onServer() + .onType(CodeSystem.class) .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java index a1cbdd421a6..bead0e39896 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java @@ -52,6 +52,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs @Autowired private SubscriptionTestUtil mySubscriptionTestUtil; + @Override @After public void after() throws Exception { super.after(); @@ -65,6 +66,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs myWebSocketClient.stop(); } + @Override @Before public void before() throws Exception { super.before(); @@ -117,7 +119,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs } @Test - public void createObservation() throws Exception { + public void createObservation() { Observation observation = new Observation(); CodeableConcept codeableConcept = new CodeableConcept(); observation.setCode(codeableConcept); @@ -141,7 +143,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs } @Test - public void createObservationThatDoesNotMatch() throws Exception { + public void createObservationThatDoesNotMatch() { Observation observation = new Observation(); CodeableConcept codeableConcept = new CodeableConcept(); observation.setCode(codeableConcept); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImgthlaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImgthlaTest.java index 9074fe58bdf..68301d49654 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImgthlaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImgthlaTest.java @@ -1,38 +1,22 @@ package ca.uhn.fhir.jpa.term; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.ValueSet; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; @RunWith(MockitoJUnitRunner.class) public class TerminologyLoaderSvcImgthlaTest { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java index ce8c0a5712b..92a09cf73e5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.util.TestUtil; @@ -134,9 +134,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); myLoader.loadLoinc(files.getFiles(), mySrd); - IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(null); - Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); + IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); + Parameters parameters = (Parameters) result.toParameters(myFhirCtx, null); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); @@ -165,9 +164,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); myLoader.loadLoinc(files.getFiles(), mySrd); - IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("17788-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(null); - Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); + IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("17788-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); + Parameters parameters = (Parameters) result.toParameters(myFhirCtx, null); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); @@ -184,10 +182,9 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); myLoader.loadLoinc(files.getFiles(), mySrd); - IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); + IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); List> properties = Lists.newArrayList(new CodeType("SCALE_TYP")); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(properties); - Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); + Parameters parameters = (Parameters) result.toParameters(myFhirCtx, properties); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index 13d3e4eb8b8..811547c066b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -52,6 +52,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + codeSystem.setName("SYSTEM NAME"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); @@ -95,7 +96,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs); return id; } @@ -114,7 +115,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentA = new TermConcept(cs, "CS2"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", "SYSTEM VERSION" , cs); return id; } @@ -146,7 +147,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { code.addPropertyString("HELLO", "12345-2"); cs.getConcepts().add(code); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs); }); } @@ -162,7 +163,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs); // Update cs = new TermCodeSystemVersion(); @@ -171,17 +172,14 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION" , cs); // Try to update to a different resource codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); - cs.setResource(table); try { - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); fail(); } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); @@ -585,7 +583,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { child.addChild(parent, RelationshipTypeEnum.ISA); try { - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION" , cs); fail(); } catch (InvalidRequestException e) { assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); @@ -708,7 +706,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { runInTransaction(()->{ ResourceTable resTable = myEntityManager.find(ResourceTable.class, csId.getIdPartAsLong()); version.setResource(resTable); - myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", version); + myTermSvc.storeNewCodeSystemVersion(csId.getIdPartAsLong(), cs.getUrl(), "My System", "SYSTEM VERSION" , version); }); org.hl7.fhir.dstu3.model.ValueSet vs = new org.hl7.fhir.dstu3.model.ValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java index 31e0c7c1936..dbd4a91eeea 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java @@ -1,14 +1,22 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.junit.*; import org.junit.rules.ExpectedException; import org.mockito.Mock; @@ -18,9 +26,12 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.Nonnull; import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; @@ -31,13 +42,12 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class); @Rule public final ExpectedException expectedException = ExpectedException.none(); + @Mock + IValueSetCodeAccumulator myValueSetCodeAccumulator; private IIdType myConceptMapId; private IIdType myExtensionalCsId; private IIdType myExtensionalVsId; - @Mock - IValueSetCodeAccumulator myValueSetCodeAccumulator; - @Before public void before() { myDaoConfig.setAllowExternalReferences(true); @@ -53,6 +63,8 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + codeSystem.setName("SYSTEM NAME"); + codeSystem.setVersion("SYSTEM VERSION"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); @@ -95,11 +107,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs); return id; } - + private void createAndPersistConceptMap() { ConceptMap conceptMap = createConceptMap(); persistConceptMap(conceptMap); @@ -157,6 +169,304 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { }); } + @Test + public void testApplyCodeSystemDeltaAdd() { + + // Create not-present + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://foo"); + cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + myCodeSystemDao.create(cs); + + CodeSystem delta = new CodeSystem(); + delta + .addConcept() + .setCode("codeA") + .setDisplay("displayA"); + delta + .addConcept() + .setCode("codeB") + .setDisplay("displayB"); + myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + + assertEquals(true, myTermSvc.findCode("http://foo", "codeA").isPresent()); + assertEquals(false, myTermSvc.findCode("http://foo", "codeZZZ").isPresent()); + + } + + /** + * This would be a good check, but there is no easy eay to do it... + */ + @Test + @Ignore + public void testApplyCodeSystemDeltaAddNotPermittedForNonExternalCodeSystem() { + + // Create not-present + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://foo"); + cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + myCodeSystemDao.create(cs); + + CodeSystem delta = new CodeSystem(); + delta + .addConcept() + .setCode("codeA") + .setDisplay("displayA"); + try { + myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + fail(); + } catch (InvalidRequestException e) { + assertEquals("", e.getMessage()); + } + + } + + @Test + public void testApplyCodeSystemDeltaAddWithoutPreExistingCodeSystem() { + + CodeSystem delta = new CodeSystem(); + delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + delta.setUrl("http://foo"); + delta.setName("Acme Lab Codes"); + delta + .addConcept() + .setCode("CBC") + .setDisplay("Complete Blood Count"); + delta + .addConcept() + .setCode("URNL") + .setDisplay("Routine Urinalysis"); + myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + params.add(CodeSystem.SP_URL, new UriParam("http://foo")); + IBundleProvider searchResult = myCodeSystemDao.search(params, mySrd); + assertEquals(1, searchResult.size().intValue()); + CodeSystem outcome = (CodeSystem) searchResult.getResources(0,1).get(0); + + assertEquals("http://foo", outcome.getUrl()); + assertEquals("Acme Lab Codes", outcome.getName()); + } + + + @Test + public void testApplyCodeSystemDeltaAddDuplicatesIgnored() { + + // Add codes + CodeSystem delta = new CodeSystem(); + delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + delta.setUrl("http://foo"); + delta.setName("Acme Lab Codes"); + delta + .addConcept() + .setCode("codea") + .setDisplay("CODEA0"); + delta + .addConcept() + .setCode("codeb") + .setDisplay("CODEB0"); + AtomicInteger outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + assertEquals(2, outcome.get()); + + // Add codes again with different display + delta = new CodeSystem(); + delta.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + delta.setUrl("http://foo"); + delta.setName("Acme Lab Codes"); + delta + .addConcept() + .setCode("codea") + .setDisplay("CODEA1"); + delta + .addConcept() + .setCode("codeb") + .setDisplay("CODEB1"); + outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + assertEquals(2, outcome.get()); + + // Add codes again with no changes + outcome = myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + assertEquals(0, outcome.get()); + } + + + @Test + public void testApplyCodeSystemDeltaAddAsChild() { + + // Create not-present + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://foo"); + cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + myCodeSystemDao.create(cs); + + CodeSystem delta = new CodeSystem(); + delta + .addConcept() + .setCode("codeA") + .setDisplay("displayA"); + delta + .addConcept() + .setCode("codeB") + .setDisplay("displayB"); + myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + + delta = new CodeSystem(); + CodeSystem.ConceptDefinitionComponent codeAA = delta + .addConcept() + .setCode("codeAA") + .setDisplay("displayAA"); + codeAA + .addConcept() + .setCode("codeAAA") + .setDisplay("displayAAA"); + myTermSvc.applyDeltaCodesystemsAdd("http://foo", "codeA", delta); + + assertEquals(true, myTermSvc.findCode("http://foo", "codeAA").isPresent()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeA"), toString("codeAA"), toString("http://foo"), null, null).getOutcome()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeA"), toString("codeAAA"), toString("http://foo"), null, null).getOutcome()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY, myTermSvc.subsumes(toString("codeAA"), toString("codeAAA"), toString("http://foo"), null, null).getOutcome()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED, myTermSvc.subsumes(toString("codeB"), toString("codeAA"), toString("http://foo"), null, null).getOutcome()); + + runInTransaction(() -> { + List 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 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 properties = output.getParameter().stream().filter(t -> t.getName().equals("property")).collect(Collectors.toList()); + assertEquals("code", properties.get(0).getPart().get(0).getName()); + assertEquals("flavour", ((CodeType) properties.get(0).getPart().get(0).getValue()).getValueAsString()); + assertEquals("value", properties.get(0).getPart().get(1).getName()); + assertEquals("Hints of lime", ((StringType) properties.get(0).getPart().get(1).getValue()).getValueAsString()); + + assertEquals("code", properties.get(1).getPart().get(0).getName()); + assertEquals("useless_sct_code", ((CodeType) properties.get(1).getPart().get(0).getValue()).getValueAsString()); + assertEquals("value", properties.get(1).getPart().get(1).getName()); + assertEquals("http://snomed.info", ((Coding) properties.get(1).getPart().get(1).getValue()).getSystem()); + assertEquals("1234567", ((Coding) properties.get(1).getPart().get(1).getValue()).getCode()); + assertEquals("Choked on large meal (finding)", ((Coding) properties.get(1).getPart().get(1).getValue()).getDisplay()); + + } + + @Test + public void testApplyCodeSystemDeltaRemove() { + + // Create not-present + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://foo"); + cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + myCodeSystemDao.create(cs); + + CodeSystem delta = new CodeSystem(); + CodeSystem.ConceptDefinitionComponent codeA = delta + .addConcept() + .setCode("codeA") + .setDisplay("displayA"); + delta + .addConcept() + .setCode("codeB") + .setDisplay("displayB"); + CodeSystem.ConceptDefinitionComponent codeAA = codeA + .addConcept() + .setCode("codeAA") + .setDisplay("displayAA"); + codeAA + .addConcept() + .setCode("codeAAA") + .setDisplay("displayAAA"); + myTermSvc.applyDeltaCodesystemsAdd("http://foo", null, delta); + + // Remove CodeB + delta = new CodeSystem(); + delta + .addConcept() + .setCode("codeB") + .setDisplay("displayB"); + myTermSvc.applyDeltaCodesystemsRemove("http://foo", delta); + + assertEquals(false, myTermSvc.findCode("http://foo", "codeB").isPresent()); + assertEquals(true, myTermSvc.findCode("http://foo", "codeA").isPresent()); + assertEquals(true, myTermSvc.findCode("http://foo", "codeAA").isPresent()); + assertEquals(true, myTermSvc.findCode("http://foo", "codeAAA").isPresent()); + + // Remove CodeA + delta = new CodeSystem(); + delta + .addConcept() + .setCode("codeA"); + myTermSvc.applyDeltaCodesystemsRemove("http://foo", delta); + + assertEquals(false, myTermSvc.findCode("http://foo", "codeB").isPresent()); + assertEquals(false, myTermSvc.findCode("http://foo", "codeA").isPresent()); + assertEquals(false, myTermSvc.findCode("http://foo", "codeAA").isPresent()); + assertEquals(false, myTermSvc.findCode("http://foo", "codeAAA").isPresent()); + + } + + + @Nonnull + private StringType toString(String theString) { + return new StringType(theString); + } + + @Test public void testCreateConceptMapWithMissingSourceSystem() { ConceptMap conceptMap = new ConceptMap(); @@ -183,7 +493,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testCreateConceptMapWithVirtualSourceSystem() { ConceptMap conceptMap = createConceptMap(); - conceptMap.getGroup().forEach(t->t.setSource(null)); + conceptMap.getGroup().forEach(t -> t.setSource(null)); conceptMap.setSource(new CanonicalType("http://hl7.org/fhir/uv/livd/StructureDefinition/loinc-livd")); persistConceptMap(conceptMap); @@ -283,6 +593,17 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { verify(myValueSetCodeAccumulator, times(9)).includeCodeWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); } + @Test + public void testValidateCode() { + createCodeSystem(); + + IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ParentWithNoChildrenA", null); + assertEquals(true, validation.isOk()); + + validation = myTermSvc.validateCode(myFhirCtx, CS_URL, "ZZZZZZZ", null); + assertEquals(false, validation.isOk()); + } + @Test public void testStoreTermCodeSystemAndChildren() throws Exception { loadAndPersistCodeSystemWithDesignations(); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs.xml index f06296cfa89..0cb1edad4a4 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs.xml @@ -1,5 +1,6 @@ + @@ -96,4 +97,4 @@ - \ No newline at end of file + diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index ae1cc3d0d6d..e97c14313d5 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -23,6 +23,17 @@ package ca.uhn.fhir.jpa.model.util; import ca.uhn.fhir.rest.api.Constants; public class JpaConstants { + + /** + * Operation name for the $apply-codesystem-delta-add operation + */ + public static final String OPERATION_APPLY_CODESYSTEM_DELTA_ADD = "$apply-codesystem-delta-add"; + + /** + * Operation name for the $apply-codesystem-delta-remove operation + */ + public static final String OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE = "$apply-codesystem-delta-remove"; + /** * Operation name for the $expunge operation */ @@ -158,6 +169,11 @@ public class JpaConstants { */ public static final String OPERATION_BINARY_ACCESS_WRITE = "$binary-access-write"; + /** + * Operation name for the "$upload-external-code-system" operation + */ + public static final String OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; + /** *

* This extension should be of type string and should be diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java index 592ed5becb0..a9d6f32725a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java @@ -1,7 +1,6 @@ package ca.uhn.fhirtest.interceptor; import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider; -import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; @@ -35,7 +34,9 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor { if (isBlank(authHeader)) { return new RuleBuilder() .deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andAllowAllResponses().andThen() - .deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andAllowAllResponses().andThen() + .deny().operation().named(JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andAllowAllResponses().andThen() + .deny().operation().named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD).atAnyLevel().andAllowAllResponses().andThen() + .deny().operation().named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE).atAnyLevel().andAllowAllResponses().andThen() .deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onServer().andAllowAllResponses().andThen() .deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andAllowAllResponses().andThen() .deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().andAllowAllResponses().andThen() diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java index 2bb55bccfe4..c66da76d646 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java @@ -445,15 +445,6 @@ public abstract class BaseMethodBinding { return theReturnTypeFromMethod.equals(IBaseResource.class) || theReturnTypeFromMethod.equals(IResource.class) || theReturnTypeFromMethod.equals(IAnyResource.class); } - private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) { - try { - String responseText = IOUtils.toString(theResponseReader); - theEx.setResponseBody(responseText); - } catch (IOException e) { - ourLog.debug("Failed to read response", e); - } - } - private static String toLogString(Class theType) { if (theType == null) { return null; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index 59be4d7f49f..7a62743b048 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -68,14 +68,15 @@ public class MethodUtil { @SuppressWarnings("unchecked") public static List getResourceParameters(final FhirContext theContext, Method theMethod, Object theProvider, RestOperationTypeEnum theRestfulOperationTypeEnum) { - List parameters = new ArrayList(); + List parameters = new ArrayList<>(); Class[] parameterTypes = theMethod.getParameterTypes(); int paramIndex = 0; for (Annotation[] annotations : theMethod.getParameterAnnotations()) { IParameter param = null; - Class parameterType = parameterTypes[paramIndex]; + Class declaredParameterType = parameterTypes[paramIndex]; + Class parameterType = declaredParameterType; Class> outerCollectionType = null; Class> innerCollectionType = null; if (TagList.class.isAssignableFrom(parameterType)) { @@ -85,11 +86,13 @@ public class MethodUtil { if (Collection.class.isAssignableFrom(parameterType)) { innerCollectionType = (Class>) parameterType; parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex); + declaredParameterType = parameterType; } if (Collection.class.isAssignableFrom(parameterType)) { outerCollectionType = innerCollectionType; innerCollectionType = (Class>) parameterType; parameterType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex); + declaredParameterType = parameterType; } if (Collection.class.isAssignableFrom(parameterType)) { throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() @@ -223,7 +226,7 @@ public class MethodUtil { param = new OperationParameter(theContext, op.name(), operationParam); if (isNotBlank(operationParam.typeName())) { Class newParameterType = theContext.getElementDefinition(operationParam.typeName()).getImplementingClass(); - if (!parameterType.isAssignableFrom(newParameterType)) { + if (!declaredParameterType.isAssignableFrom(newParameterType)) { throw new ConfigurationException("Non assignable parameter typeName=\"" + operationParam.typeName() + "\" specified on method " + theMethod); } parameterType = newParameterType; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java index c1c4bf0a226..01886322201 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -72,7 +73,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { private boolean myManualResponseMode; protected OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, - boolean theIdempotent, String theOperationName, Class theOperationType, + boolean theIdempotent, String theOperationName, Class theOperationType, String theOperationTypeName, OperationParam[] theReturnParams, BundleTypeEnum theBundleType) { super(theReturnResourceType, theMethod, theContext, theProvider); @@ -99,12 +100,18 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { } myName = theOperationName; - if (theReturnTypeFromRp != null) { - setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName()); - } else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) { - setResourceName(theContext.getResourceDefinition(theOperationType).getName()); - } else { - setResourceName(null); + try { + if (theReturnTypeFromRp != null) { + setResourceName(theContext.getResourceDefinition(theReturnTypeFromRp).getName()); + } else if (Modifier.isAbstract(theOperationType.getModifiers()) == false) { + setResourceName(theContext.getResourceDefinition(theOperationType).getName()); + } else if (isNotBlank(theOperationTypeName)) { + setResourceName(theContext.getResourceDefinition(theOperationTypeName).getName()); + } else { + setResourceName(null); + } + } catch (DataFormatException e) { + throw new ConfigurationException("Failed to bind method " + theMethod + " - " + e.getMessage(), e); } if (theMethod.getReturnType().equals(IBundleProvider.class)) { @@ -160,7 +167,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { */ public OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Operation theAnnotation) { - this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.returnParameters(), + this(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, theAnnotation.idempotent(), theAnnotation.name(), theAnnotation.type(), theAnnotation.typeName(), theAnnotation.returnParameters(), theAnnotation.bundleType()); myManualRequestMode = theAnnotation.manualRequest(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java index b5aaeaf4eed..4afac45b0cb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java @@ -187,7 +187,7 @@ public class OperationParameter implements IParameter { mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType); myConverter = new OperationParamConverter(); } else { - throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName()); + throw new ConfigurationException("Invalid type for @OperationParam on method " + theMethod + ": " + myParameterType.getName()); } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ValidateMethodBindingDstu2Plus.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ValidateMethodBindingDstu2Plus.java index 3bd028dc82a..dde4cf854c7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ValidateMethodBindingDstu2Plus.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ValidateMethodBindingDstu2Plus.java @@ -37,7 +37,7 @@ public class ValidateMethodBindingDstu2Plus extends OperationMethodBinding { public ValidateMethodBindingDstu2Plus(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, Validate theAnnotation) { - super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), new OperationParam[0], BundleTypeEnum.COLLECTION); + super(theReturnResourceType, theReturnTypeFromRp, theMethod, theContext, theProvider, true, Constants.EXTOP_VALIDATE, theAnnotation.type(), null, new OperationParam[0], BundleTypeEnum.COLLECTION); List newParams = new ArrayList(); int idx = 0; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java index f4e9c4c2b60..84867c5a0ed 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java @@ -276,4 +276,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport { return new CodeValidationResult(OperationOutcome.IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode); + } + } diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/IValidationSupport.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/IValidationSupport.java index 89bb14ff8ce..eca31514963 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/IValidationSupport.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/IValidationSupport.java @@ -100,7 +100,7 @@ public interface IValidationSupport @Override CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); - public class CodeValidationResult extends IContextValidationSupport.CodeValidationResult { + class CodeValidationResult extends IContextValidationSupport.CodeValidationResult { public CodeValidationResult(ConceptDefinitionComponent theNext) { super(theNext); @@ -114,6 +114,15 @@ public interface IValidationSupport super(severity, message, definition); } + @Override + protected String getDisplay() { + String retVal = null; + if (isOk()) { + retVal = asConceptDefinition().getDisplay(); + } + return retVal; + } + } } diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/PrePopulatedValidationSupport.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/PrePopulatedValidationSupport.java index a1d074e7389..32524632389 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/PrePopulatedValidationSupport.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/PrePopulatedValidationSupport.java @@ -138,4 +138,9 @@ public class PrePopulatedValidationSupport implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + } diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/ValidationSupportChain.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/ValidationSupportChain.java index fe3bdc92913..a3ef3a3076f 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/ValidationSupportChain.java @@ -143,4 +143,15 @@ public class ValidationSupportChain implements IValidationSupport { return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theContext, theSystem)) { + return next.lookupCode(theContext, theSystem, theCode); + } + } + return null; + } + + } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java index 70783346d7b..f90e6a46a8e 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/DefaultProfileValidationSupport.java @@ -24,274 +24,284 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class DefaultProfileValidationSupport implements IValidationSupport { - private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/"; - private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/"; - private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/"; + private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/"; + private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/"; + private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class); - private Map myCodeSystems; - private Map myStructureDefinitions; - private Map myValueSets; + private Map myCodeSystems; + private Map myStructureDefinitions; + private Map myValueSets; - @Override - public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { - ValueSetExpansionComponent retVal = new ValueSetExpansionComponent(); + @Override + public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { + ValueSetExpansionComponent retVal = new ValueSetExpansionComponent(); - Set wantCodes = new HashSet<>(); - for (ConceptReferenceComponent next : theInclude.getConcept()) { - wantCodes.add(next.getCode()); - } + Set wantCodes = new HashSet<>(); + for (ConceptReferenceComponent next : theInclude.getConcept()) { + wantCodes.add(next.getCode()); + } - CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem()); - if (system != null) { - List concepts = system.getConcept(); - addConcepts(theInclude, retVal, wantCodes, concepts); - } + CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem()); + if (system != null) { + List concepts = system.getConcept(); + addConcepts(theInclude, retVal, wantCodes, concepts); + } - for (UriType next: theInclude.getValueSet()) { - ValueSet vs = myValueSets.get(defaultString(next.getValueAsString())); - if (vs != null) { - for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) { - ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude); - retVal.getContains().addAll(contents.getContains()); - } - } - } + for (UriType next : theInclude.getValueSet()) { + ValueSet vs = myValueSets.get(defaultString(next.getValueAsString())); + if (vs != null) { + for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) { + ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude); + retVal.getContains().addAll(contents.getContains()); + } + } + } - return retVal; - } + return retVal; + } - private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set theWantCodes, List theConcepts) { - for (ConceptDefinitionComponent next : theConcepts) { - if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) { - theRetVal - .addContains() - .setSystem(theInclude.getSystem()) - .setCode(next.getCode()) - .setDisplay(next.getDisplay()); - } - addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept()); - } - } + private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set theWantCodes, List theConcepts) { + for (ConceptDefinitionComponent next : theConcepts) { + if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) { + theRetVal + .addContains() + .setSystem(theInclude.getSystem()) + .setCode(next.getCode()) + .setDisplay(next.getDisplay()); + } + addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept()); + } + } - @Override - public List fetchAllConformanceResources(FhirContext theContext) { - ArrayList retVal = new ArrayList<>(); - retVal.addAll(myCodeSystems.values()); - retVal.addAll(myStructureDefinitions.values()); - retVal.addAll(myValueSets.values()); - return retVal; - } + @Override + public List fetchAllConformanceResources(FhirContext theContext) { + ArrayList retVal = new ArrayList<>(); + retVal.addAll(myCodeSystems.values()); + retVal.addAll(myStructureDefinitions.values()); + retVal.addAll(myValueSets.values()); + return retVal; + } - @Override - public List fetchAllStructureDefinitions(FhirContext theContext) { - return new ArrayList(provideStructureDefinitionMap(theContext).values()); - } + @Override + public List fetchAllStructureDefinitions(FhirContext theContext) { + return new ArrayList(provideStructureDefinitionMap(theContext).values()); + } - @Override - public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) { - return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true); - } + @Override + public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) { + return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true); + } - private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) { - synchronized (this) { - Map codeSystems = myCodeSystems; - Map valueSets = myValueSets; - if (codeSystems == null || valueSets == null) { - codeSystems = new HashMap(); - valueSets = new HashMap(); + private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) { + synchronized (this) { + Map codeSystems = myCodeSystems; + Map valueSets = myValueSets; + if (codeSystems == null || valueSets == null) { + codeSystems = new HashMap(); + valueSets = new HashMap(); - loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/valuesets.xml"); - loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml"); - loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml"); + loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/valuesets.xml"); + loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml"); + loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml"); - myCodeSystems = codeSystems; - myValueSets = valueSets; - } + myCodeSystems = codeSystems; + myValueSets = valueSets; + } - if (codeSystem) { - return codeSystems.get(theSystem); - } else { - return valueSets.get(theSystem); - } - } - } + if (codeSystem) { + return codeSystems.get(theSystem); + } else { + return valueSets.get(theSystem); + } + } + } - @SuppressWarnings("unchecked") - @Override - public T fetchResource(FhirContext theContext, Class theClass, String theUri) { - Validate.notBlank(theUri, "theUri must not be null or blank"); + @SuppressWarnings("unchecked") + @Override + public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + Validate.notBlank(theUri, "theUri must not be null or blank"); - if (theClass.equals(StructureDefinition.class)) { - return (T) fetchStructureDefinition(theContext, theUri); - } + if (theClass.equals(StructureDefinition.class)) { + return (T) fetchStructureDefinition(theContext, theUri); + } - if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) { - return (T) fetchValueSet(theContext, theUri); - } + if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) { + return (T) fetchValueSet(theContext, theUri); + } - return null; - } + return null; + } - @Override - public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) { - String url = theUrl; - if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) { - // no change - } else if (url.indexOf('/') == -1) { - url = URL_PREFIX_STRUCTURE_DEFINITION + url; - } else if (StringUtils.countMatches(url, '/') == 1) { - url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url; - } - Map map = provideStructureDefinitionMap(theContext); - StructureDefinition retVal = map.get(url); + @Override + public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) { + String url = theUrl; + if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) { + // no change + } else if (url.indexOf('/') == -1) { + url = URL_PREFIX_STRUCTURE_DEFINITION + url; + } else if (StringUtils.countMatches(url, '/') == 1) { + url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url; + } + Map map = provideStructureDefinitionMap(theContext); + StructureDefinition retVal = map.get(url); - if (retVal == null && url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) { - String tryUrl = URL_PREFIX_STRUCTURE_DEFINITION + StringUtils.capitalize(url.substring(URL_PREFIX_STRUCTURE_DEFINITION.length())); - retVal = map.get(tryUrl); - } + if (retVal == null && url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) { + String tryUrl = URL_PREFIX_STRUCTURE_DEFINITION + StringUtils.capitalize(url.substring(URL_PREFIX_STRUCTURE_DEFINITION.length())); + retVal = map.get(tryUrl); + } - return retVal; - } + return retVal; + } - @Override - public ValueSet fetchValueSet(FhirContext theContext, String uri) { - return (ValueSet) fetchCodeSystemOrValueSet(theContext, uri, false); - } + @Override + public ValueSet fetchValueSet(FhirContext theContext, String uri) { + return (ValueSet) fetchCodeSystemOrValueSet(theContext, uri, false); + } - public void flush() { - myCodeSystems = null; - myStructureDefinitions = null; - } + public void flush() { + myCodeSystems = null; + myStructureDefinitions = null; + } - @Override - public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { - CodeSystem cs = fetchCodeSystem(theContext, theSystem); - return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT; - } + @Override + public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { + CodeSystem cs = fetchCodeSystem(theContext, theSystem); + return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT; + } - private void loadCodeSystems(FhirContext theContext, Map theCodeSystems, Map theValueSets, String theClasspath) { - ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath); - InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); - InputStreamReader reader = null; - if (inputStream != null) { - try { - reader = new InputStreamReader(inputStream, Charsets.UTF_8); + private void loadCodeSystems(FhirContext theContext, Map theCodeSystems, Map theValueSets, String theClasspath) { + ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath); + InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); + InputStreamReader reader = null; + if (inputStream != null) { + try { + reader = new InputStreamReader(inputStream, Charsets.UTF_8); - Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); - for (BundleEntryComponent next : bundle.getEntry()) { - if (next.getResource() instanceof CodeSystem) { - CodeSystem nextValueSet = (CodeSystem) next.getResource(); - nextValueSet.getText().setDivAsString(""); - String system = nextValueSet.getUrl(); - if (isNotBlank(system)) { - theCodeSystems.put(system, nextValueSet); - } - } else if (next.getResource() instanceof ValueSet) { - ValueSet nextValueSet = (ValueSet) next.getResource(); - nextValueSet.getText().setDivAsString(""); - String system = nextValueSet.getUrl(); - if (isNotBlank(system)) { - theValueSets.put(system, nextValueSet); - } - } - } - } finally { - IOUtils.closeQuietly(reader); - IOUtils.closeQuietly(inputStream); - } - } else { - ourLog.warn("Unable to load resource: {}", theClasspath); - } - } + Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); + for (BundleEntryComponent next : bundle.getEntry()) { + if (next.getResource() instanceof CodeSystem) { + CodeSystem nextValueSet = (CodeSystem) next.getResource(); + nextValueSet.getText().setDivAsString(""); + String system = nextValueSet.getUrl(); + if (isNotBlank(system)) { + theCodeSystems.put(system, nextValueSet); + } + } else if (next.getResource() instanceof ValueSet) { + ValueSet nextValueSet = (ValueSet) next.getResource(); + nextValueSet.getText().setDivAsString(""); + String system = nextValueSet.getUrl(); + if (isNotBlank(system)) { + theValueSets.put(system, nextValueSet); + } + } + } + } finally { + IOUtils.closeQuietly(reader); + IOUtils.closeQuietly(inputStream); + } + } else { + ourLog.warn("Unable to load resource: {}", theClasspath); + } + } - private void loadStructureDefinitions(FhirContext theContext, Map theCodeSystems, String theClasspath) { - ourLog.info("Loading structure definitions from classpath: {}", theClasspath); - InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); - if (valuesetText != null) { - InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8); + private void loadStructureDefinitions(FhirContext theContext, Map theCodeSystems, String theClasspath) { + ourLog.info("Loading structure definitions from classpath: {}", theClasspath); + InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); + if (valuesetText != null) { + InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8); - Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); - for (BundleEntryComponent next : bundle.getEntry()) { - if (next.getResource() instanceof StructureDefinition) { - StructureDefinition nextSd = (StructureDefinition) next.getResource(); - nextSd.getText().setDivAsString(""); - String system = nextSd.getUrl(); - if (isNotBlank(system)) { - theCodeSystems.put(system, nextSd); - } - } - } - } else { - ourLog.warn("Unable to load resource: {}", theClasspath); - } - } + Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); + for (BundleEntryComponent next : bundle.getEntry()) { + if (next.getResource() instanceof StructureDefinition) { + StructureDefinition nextSd = (StructureDefinition) next.getResource(); + nextSd.getText().setDivAsString(""); + String system = nextSd.getUrl(); + if (isNotBlank(system)) { + theCodeSystems.put(system, nextSd); + } + } + } + } else { + ourLog.warn("Unable to load resource: {}", theClasspath); + } + } - private Map provideStructureDefinitionMap(FhirContext theContext) { - Map structureDefinitions = myStructureDefinitions; - if (structureDefinitions == null) { - structureDefinitions = new HashMap(); + private Map provideStructureDefinitionMap(FhirContext theContext) { + Map structureDefinitions = myStructureDefinitions; + if (structureDefinitions == null) { + structureDefinitions = new HashMap(); - loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml"); - loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-types.xml"); - loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-others.xml"); - loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml"); + loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml"); + loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-types.xml"); + loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/profile/profiles-others.xml"); + loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml"); - myStructureDefinitions = structureDefinitions; - } - return structureDefinitions; - } + myStructureDefinitions = structureDefinitions; + } + return structureDefinitions; + } - private CodeValidationResult testIfConceptIsInList(String theCode, List conceptList, boolean theCaseSensitive) { - String code = theCode; - if (theCaseSensitive == false) { - code = code.toUpperCase(); - } + private CodeValidationResult testIfConceptIsInList(CodeSystem theCodeSystem, String theCode, List conceptList, boolean theCaseSensitive) { + String code = theCode; + if (theCaseSensitive == false) { + code = code.toUpperCase(); + } - return testIfConceptIsInListInner(conceptList, theCaseSensitive, code); - } + return testIfConceptIsInListInner(theCodeSystem, conceptList, theCaseSensitive, code); + } - private CodeValidationResult testIfConceptIsInListInner(List conceptList, boolean theCaseSensitive, String code) { - CodeValidationResult retVal = null; - for (ConceptDefinitionComponent next : conceptList) { - String nextCandidate = next.getCode(); - if (theCaseSensitive == false) { - nextCandidate = nextCandidate.toUpperCase(); - } - if (nextCandidate.equals(code)) { - retVal = new CodeValidationResult(next); - break; - } + private CodeValidationResult testIfConceptIsInListInner(CodeSystem theCodeSystem, List conceptList, boolean theCaseSensitive, String code) { + CodeValidationResult retVal = null; + for (ConceptDefinitionComponent next : conceptList) { + String nextCandidate = next.getCode(); + if (theCaseSensitive == false) { + nextCandidate = nextCandidate.toUpperCase(); + } + if (nextCandidate.equals(code)) { + retVal = new CodeValidationResult(next); + break; + } - // recurse - retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive); - if (retVal != null) { - break; - } - } + // recurse + retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive); + if (retVal != null) { + break; + } + } - return retVal; - } + if (retVal != null) { + retVal.setCodeSystemName(theCodeSystem.getName()); + retVal.setCodeSystemVersion(theCodeSystem.getVersion()); + } - @Override - public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { - CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem); - if (cs != null) { - boolean caseSensitive = true; - if (cs.hasCaseSensitive()) { - caseSensitive = cs.getCaseSensitive(); - } + return retVal; + } - CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive); + @Override + public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { + CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem); + if (cs != null) { + boolean caseSensitive = true; + if (cs.hasCaseSensitive()) { + caseSensitive = cs.getCaseSensitive(); + } - if (retVal != null) { - return retVal; - } - } + CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive); - return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); - } + if (retVal != null) { + return retVal; + } + } + + return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); + } + + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode); + } @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java index 5946f57dfbe..7b6c38d73b1 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java @@ -1,43 +1,39 @@ package org.hl7.fhir.dstu3.hapi.ctx; -import java.util.List; - +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.IContextValidationSupport; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; -import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.support.IContextValidationSupport; +import java.util.List; public interface IValidationSupport - extends ca.uhn.fhir.context.support.IContextValidationSupport { + extends ca.uhn.fhir.context.support.IContextValidationSupport { - /** - * Expands the given portion of a ValueSet - * - * @param theInclude - * The portion to include - * @return The expansion - */ - @Override - ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude); + /** + * Expands the given portion of a ValueSet + * + * @param theInclude The portion to include + * @return The expansion + */ + @Override + ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude); - /** - * Load and return all possible structure definitions - */ - @Override - List fetchAllStructureDefinitions(FhirContext theContext); + /** + * Load and return all possible structure definitions + */ + @Override + List fetchAllStructureDefinitions(FhirContext theContext); /** * Fetch a code system by Uri * - * @param uri - * Canonical Uri of the code system + * @param uri Canonical Uri of the code system * @return The valueset (must not be null, but can be an empty ValueSet) */ @Override @@ -46,82 +42,68 @@ public interface IValidationSupport /** * Fetch a valueset by Uri * - * @param uri - * Canonical Uri of the ValueSet + * @param uri Canonical Uri of the ValueSet * @return The valueset (must not be null, but can be an empty ValueSet) */ ValueSet fetchValueSet(FhirContext theContext, String uri); - /** - * Loads a resource needed by the validation (a StructureDefinition, or a - * ValueSet) - * - * @param theContext - * The HAPI FHIR Context object current in use by the validator - * @param theClass - * The type of the resource to load - * @param theUri - * The resource URI - * @return Returns the resource, or null if no resource with the - * given URI can be found - */ - @Override - T fetchResource(FhirContext theContext, Class theClass, String theUri); + @Override + StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl); - @Override - StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl); + /** + * Returns true if codes in the given code system can be expanded + * or validated + * + * @param theSystem The URI for the code system, e.g. "http://loinc.org" + * @return Returns true if codes in the given code system can be + * validated + */ + @Override + boolean isCodeSystemSupported(FhirContext theContext, String theSystem); - /** - * Returns true if codes in the given code system can be expanded - * or validated - * - * @param theSystem - * The URI for the code system, e.g. "http://loinc.org" - * @return Returns true if codes in the given code system can be - * validated - */ - @Override - boolean isCodeSystemSupported(FhirContext theContext, String theSystem); - - /** - * Validates that the given code exists and if possible returns a display - * name. This method is called to check codes which are found in "example" - * binding fields (e.g. Observation.code in the default profile. - * - * @param theCodeSystem - * The code system, e.g. "http://loinc.org" - * @param theCode - * The code, e.g. "1234-5" - * @param theDisplay - * The display name, if it should also be validated - * @return Returns a validation result object - */ - @Override - CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); + /** + * Validates that the given code exists and if possible returns a display + * name. This method is called to check codes which are found in "example" + * binding fields (e.g. Observation.code in the default profile. + * + * @param theCodeSystem The code system, e.g. "http://loinc.org" + * @param theCode The code, e.g. "1234-5" + * @param theDisplay The display name, if it should also be validated + * @return Returns a validation result object + */ + @Override + CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); /** * Generate a snapshot from the given differential profile. * - * @param theInput - * @param theUrl * @return Returns null if this module does not know how to handle this request */ StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName); class CodeValidationResult extends IContextValidationSupport.CodeValidationResult { - public CodeValidationResult(ConceptDefinitionComponent theNext) { - super(theNext); - } + public CodeValidationResult(ConceptDefinitionComponent theNext) { + super(theNext); + } - public CodeValidationResult(IssueSeverity theSeverity, String theMessage) { - super(theSeverity, theMessage); - } + public CodeValidationResult(IssueSeverity theSeverity, String theMessage) { + super(theSeverity, theMessage); + } - public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) { - super(severity, message, definition); - } + public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) { + super(severity, message, definition); + } - } + @Override + protected String getDisplay() { + String retVal = null; + if (isOk()) { + retVal = asConceptDefinition().getDisplay(); + } + return retVal; + } + + } } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/DefaultProfileValidationSupport.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/DefaultProfileValidationSupport.java index 30a6e2a5ce8..bdaa609f785 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/DefaultProfileValidationSupport.java @@ -257,16 +257,16 @@ public class DefaultProfileValidationSupport implements IValidationSupport { return structureDefinitions; } - private CodeValidationResult testIfConceptIsInList(String theCode, List conceptList, boolean theCaseSensitive) { + private CodeValidationResult testIfConceptIsInList(CodeSystem theCodeSystem, String theCode, List conceptList, boolean theCaseSensitive) { String code = theCode; if (theCaseSensitive == false) { code = code.toUpperCase(); } - return testIfConceptIsInListInner(conceptList, theCaseSensitive, code); + return testIfConceptIsInListInner(theCodeSystem, conceptList, theCaseSensitive, code); } - private CodeValidationResult testIfConceptIsInListInner(List conceptList, boolean theCaseSensitive, String code) { + private CodeValidationResult testIfConceptIsInListInner(CodeSystem theCodeSystem, List conceptList, boolean theCaseSensitive, String code) { CodeValidationResult retVal = null; for (ConceptDefinitionComponent next : conceptList) { String nextCandidate = next.getCode(); @@ -279,12 +279,17 @@ public class DefaultProfileValidationSupport implements IValidationSupport { } // recurse - retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive); + retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive); if (retVal != null) { break; } } + if (retVal != null) { + retVal.setCodeSystemName(theCodeSystem.getName()); + retVal.setCodeSystemVersion(theCodeSystem.getVersion()); + } + return retVal; } @@ -297,7 +302,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport { caseSensitive = cs.getCaseSensitive(); } - CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive); + CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive); if (retVal != null) { return retVal; @@ -307,4 +312,9 @@ public class DefaultProfileValidationSupport implements IValidationSupport { return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode); + } + } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/IValidationSupport.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/IValidationSupport.java index 705e6b399a1..1473f518b9c 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/IValidationSupport.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/IValidationSupport.java @@ -112,6 +112,15 @@ public interface IValidationSupport super(severity, message, definition); } + @Override + protected String getDisplay() { + String retVal = null; + if (isOk()) { + retVal = asConceptDefinition().getDisplay(); + } + return retVal; + } + } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java index 33c514dbf9a..79a25080d06 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -29,10 +30,11 @@ import org.junit.*; import javax.servlet.ServletException; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.*; public class OperationGenericServer2R4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServer2R4Test.class); @@ -41,6 +43,7 @@ public class OperationGenericServer2R4Test { private static IdType ourLastId; private static Object ourLastParam1; private static Object ourLastParam2; + private static Object ourLastParam3; private static Parameters ourLastResourceParam; private int myPort; private Server myServer; @@ -49,6 +52,7 @@ public class OperationGenericServer2R4Test { public void before() { ourLastParam1 = null; ourLastParam2 = null; + ourLastParam3=null; ourLastId = null; ourLastResourceParam = null; } @@ -112,6 +116,65 @@ public class OperationGenericServer2R4Test { } + @SuppressWarnings("unchecked") + @Test + public void testDeclarativeStringTypedParameters() throws Exception { + + @SuppressWarnings("unused") + class PatientProvider implements IResourceProvider { + + @Override + public Class 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 theParam1, + @OperationParam(name = "PARAM2", min = 1, max = OperationParam.MAX_UNLIMITED, typeName = "string") List> theParam2, + @OperationParam(name = "PARAM3", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "attachment") List 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 param2 = (List) ourLastParam2; + assertEquals("PARAM2val", param2.get(0).getValue()); + } + + } + @Test public void testDeclarativeTypedParametersInvalid() throws Exception { @@ -138,10 +201,71 @@ public class OperationGenericServer2R4Test { fail(); } catch (ServletException e) { ConfigurationException ce = (ConfigurationException) e.getCause(); - assertEquals("Failure scanning class PatientProvider: Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PatientProvider.opInstance(org.hl7.fhir.instance.model.api.ICompositeType)", ce.getMessage()); + assertThat(ce.getMessage(), containsString("Failure scanning class PatientProvider: Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test")); } } + + @Test + public void testTypeOperationWithTypeDeclaredByName() throws Exception { + + @SuppressWarnings("unused") + class PlainProvider { + + @Operation(name = "$OP_INSTANCE", typeName = "Patient", idempotent = true) + public Parameters opInstance( + @ResourceParam() IBaseResource theResourceParam, + @IdParam IdType theId + ) { + + ourLastId = theId; + + Parameters retVal = new Parameters(); + retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); + return retVal; + } + + } + + PlainProvider provider = new PlainProvider(); + startServer(provider); + + HttpGet httpPost = new HttpGet("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + try (CloseableHttpResponse status = ourClient.execute(httpPost)) { + assertEquals(200, status.getStatusLine().getStatusCode()); + String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(response); + status.getEntity().getContent().close(); + + assertEquals("123", ourLastId.getIdPart()); + } + + } + + @Test + public void testTypeOperationWithInvalidType() throws Exception { + + @SuppressWarnings("unused") + class PlainProvider { + + @Operation(name = "$OP_INSTANCE", typeName = "FOO", idempotent = true) + public Parameters opInstance() { + return null; + } + + } + + PlainProvider provider = new PlainProvider(); + try { + startServer(provider); + fail(); + } catch (ServletException e) { + Throwable cause = e.getRootCause(); + assertEquals("Failure scanning class PlainProvider: Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage()); + } + } + + private void startServer(Object theProvider) throws Exception { myServer = new Server(0); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java index c7617181893..c3c7641ccce 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java @@ -915,7 +915,7 @@ public class OperationServerR4Test { } @Operation(name= "$manualResponseWithPrimitiveParam", idempotent = true, global = true, manualResponse = true) - public void binaryAccess( + public void manualResponseWithPrimitiveParam( @IdParam IIdType theResourceId, @OperationParam(name="path", min = 1, max = 1) IPrimitiveType thePath, ServletRequestDetails theRequestDetails, diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/CachingValidationSupport.java index 8b6799fc654..a336943cbd7 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/CachingValidationSupport.java @@ -70,6 +70,11 @@ public class CachingValidationSupport implements IValidationSupport { return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return myWrap.lookupCode(theContext, theSystem, theCode); + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { return myWrap.generateSnapshot(theInput, theUrl, theName); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/PrePopulatedValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/PrePopulatedValidationSupport.java index 956d46db324..23b207fef1b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/PrePopulatedValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/PrePopulatedValidationSupport.java @@ -185,6 +185,11 @@ public class PrePopulatedValidationSupport implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) { return null; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/SnapshotGeneratingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/SnapshotGeneratingValidationSupport.java index 33603ce8722..04cf100611b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/SnapshotGeneratingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/SnapshotGeneratingValidationSupport.java @@ -3,8 +3,6 @@ package org.hl7.fhir.dstu3.hapi.validation; import ca.uhn.fhir.context.*; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.dstu3.conformance.ProfileUtilities; import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; @@ -13,6 +11,8 @@ import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.ElementDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.validation.ValidationMessage; import java.util.ArrayList; @@ -95,6 +95,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider { @Override public boolean isDatatype(String typeSimple) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java index 5ca537433bb..d465be2dba7 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java @@ -170,6 +170,16 @@ public class ValidationSupportChain implements IValidationSupport { return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theContext, theSystem)) { + return next.lookupCode(theContext, theSystem, theCode); + } + } + return null; + } + @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) { StructureDefinition outcome = null; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/CachingValidationSupport.java index 1d12ce72cca..e0c057631df 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/CachingValidationSupport.java @@ -75,4 +75,9 @@ public class CachingValidationSupport implements IValidationSupport { public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { return myWrap.validateCode(theContext, theCodeSystem, theCode, theDisplay); } + + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return myWrap.lookupCode(theContext, theSystem, theCode); + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/PrePopulatedValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/PrePopulatedValidationSupport.java index a940490c44c..db4f2401ce8 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/PrePopulatedValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/PrePopulatedValidationSupport.java @@ -24,129 +24,129 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; */ public class PrePopulatedValidationSupport implements IValidationSupport { - private Map myCodeSystems; - private Map myStructureDefinitions; - private Map myValueSets; + private Map myCodeSystems; + private Map myStructureDefinitions; + private Map myValueSets; - /** - * Constructor - */ - public PrePopulatedValidationSupport() { - myStructureDefinitions = new HashMap<>(); - myValueSets = new HashMap<>(); - myCodeSystems = new HashMap<>(); - } + /** + * Constructor + */ + public PrePopulatedValidationSupport() { + myStructureDefinitions = new HashMap<>(); + myValueSets = new HashMap<>(); + myCodeSystems = new HashMap<>(); + } - /** - * Constructor - * - * @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and - * values are the resource itself. - * @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are - * the resource itself. - * @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are - * the resource itself. - */ - public PrePopulatedValidationSupport(Map theStructureDefinitions, Map theValueSets, Map theCodeSystems) { - myStructureDefinitions = theStructureDefinitions; - myValueSets = theValueSets; - myCodeSystems = theCodeSystems; - } + /** + * Constructor + * + * @param theStructureDefinitions The StructureDefinitions to be returned by this module. Keys are the logical URL for the resource, and + * values are the resource itself. + * @param theValueSets The ValueSets to be returned by this module. Keys are the logical URL for the resource, and values are + * the resource itself. + * @param theCodeSystems The CodeSystems to be returned by this module. Keys are the logical URL for the resource, and values are + * the resource itself. + */ + public PrePopulatedValidationSupport(Map theStructureDefinitions, Map theValueSets, Map theCodeSystems) { + myStructureDefinitions = theStructureDefinitions; + myValueSets = theValueSets; + myCodeSystems = theCodeSystems; + } - /** - * Add a new CodeSystem resource which will be available to the validator. Note that - * {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this - * value will be used as the logical URL. - *

- * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), - * it will be stored in three ways: - *

    - *
  • Extension
  • - *
  • StructureDefinition/Extension
  • - *
  • http://hl7.org/StructureDefinition/Extension
  • - *
- *

- */ - public void addCodeSystem(CodeSystem theCodeSystem) { - Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value"); - addToMap(theCodeSystem, myCodeSystems, theCodeSystem.getUrl()); - } + /** + * Add a new CodeSystem resource which will be available to the validator. Note that + * {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this + * value will be used as the logical URL. + *

+ * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), + * it will be stored in three ways: + *

    + *
  • Extension
  • + *
  • StructureDefinition/Extension
  • + *
  • http://hl7.org/StructureDefinition/Extension
  • + *
+ *

+ */ + public void addCodeSystem(CodeSystem theCodeSystem) { + Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value"); + addToMap(theCodeSystem, myCodeSystems, theCodeSystem.getUrl()); + } - /** - * Add a new StructureDefinition resource which will be available to the validator. Note that - * {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this - * value will be used as the logical URL. - *

- * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), - * it will be stored in three ways: - *

    - *
  • Extension
  • - *
  • StructureDefinition/Extension
  • - *
  • http://hl7.org/StructureDefinition/Extension
  • - *
- *

- */ - public void addStructureDefinition(StructureDefinition theStructureDefinition) { - Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value"); - addToMap(theStructureDefinition, myStructureDefinitions, theStructureDefinition.getUrl()); - } + /** + * Add a new StructureDefinition resource which will be available to the validator. Note that + * {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this + * value will be used as the logical URL. + *

+ * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), + * it will be stored in three ways: + *

    + *
  • Extension
  • + *
  • StructureDefinition/Extension
  • + *
  • http://hl7.org/StructureDefinition/Extension
  • + *
+ *

+ */ + public void addStructureDefinition(StructureDefinition theStructureDefinition) { + Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value"); + addToMap(theStructureDefinition, myStructureDefinitions, theStructureDefinition.getUrl()); + } - private void addToMap(T theStructureDefinition, Map map, String theUrl) { - if (isNotBlank(theUrl)) { - map.put(theUrl, theStructureDefinition); + private void addToMap(T theStructureDefinition, Map map, String theUrl) { + if (isNotBlank(theUrl)) { + map.put(theUrl, theStructureDefinition); - int lastSlashIdx = theUrl.lastIndexOf('/'); - if (lastSlashIdx != -1) { - map.put(theUrl.substring(lastSlashIdx + 1), theStructureDefinition); - int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1); - if (previousSlashIdx != -1) { - map.put(theUrl.substring(previousSlashIdx + 1), theStructureDefinition); - } - } + int lastSlashIdx = theUrl.lastIndexOf('/'); + if (lastSlashIdx != -1) { + map.put(theUrl.substring(lastSlashIdx + 1), theStructureDefinition); + int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1); + if (previousSlashIdx != -1) { + map.put(theUrl.substring(previousSlashIdx + 1), theStructureDefinition); + } + } - } - } + } + } - /** - * Add a new ValueSet resource which will be available to the validator. Note that - * {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this - * value will be used as the logical URL. - *

- * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), - * it will be stored in three ways: - *

    - *
  • Extension
  • - *
  • StructureDefinition/Extension
  • - *
  • http://hl7.org/StructureDefinition/Extension
  • - *
- *

- */ - public void addValueSet(ValueSet theValueSet) { - Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value"); - addToMap(theValueSet, myValueSets, theValueSet.getUrl()); - } + /** + * Add a new ValueSet resource which will be available to the validator. Note that + * {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this + * value will be used as the logical URL. + *

+ * Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension), + * it will be stored in three ways: + *

    + *
  • Extension
  • + *
  • StructureDefinition/Extension
  • + *
  • http://hl7.org/StructureDefinition/Extension
  • + *
+ *

+ */ + public void addValueSet(ValueSet theValueSet) { + Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value"); + addToMap(theValueSet, myValueSets, theValueSet.getUrl()); + } - @Override - public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { - return null; - } + @Override + public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { + return null; + } - @Override - public List fetchAllConformanceResources(FhirContext theContext) { - ArrayList retVal = new ArrayList<>(); - retVal.addAll(myCodeSystems.values()); - retVal.addAll(myStructureDefinitions.values()); - retVal.addAll(myValueSets.values()); - return retVal; - } + @Override + public List fetchAllConformanceResources(FhirContext theContext) { + ArrayList retVal = new ArrayList<>(); + retVal.addAll(myCodeSystems.values()); + retVal.addAll(myStructureDefinitions.values()); + retVal.addAll(myValueSets.values()); + return retVal; + } - @Override - public List fetchAllStructureDefinitions(FhirContext theContext) { - return new ArrayList(myStructureDefinitions.values()); - } + @Override + public List fetchAllStructureDefinitions(FhirContext theContext) { + return new ArrayList(myStructureDefinitions.values()); + } - @Override + @Override public CodeSystem fetchCodeSystem(FhirContext theContext, String uri) { return myCodeSystems.get(uri); } @@ -158,29 +158,29 @@ public class PrePopulatedValidationSupport implements IValidationSupport { @SuppressWarnings("unchecked") - @Override - public T fetchResource(FhirContext theContext, Class theClass, String theUri) { - if (theClass.equals(StructureDefinition.class)) { - return (T) myStructureDefinitions.get(theUri); - } - if (theClass.equals(ValueSet.class)) { - return (T) myValueSets.get(theUri); - } - if (theClass.equals(CodeSystem.class)) { - return (T) myCodeSystems.get(theUri); - } - return null; - } + @Override + public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + if (theClass.equals(StructureDefinition.class)) { + return (T) myStructureDefinitions.get(theUri); + } + if (theClass.equals(ValueSet.class)) { + return (T) myValueSets.get(theUri); + } + if (theClass.equals(CodeSystem.class)) { + return (T) myCodeSystems.get(theUri); + } + return null; + } - @Override - public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) { - return myStructureDefinitions.get(theUrl); - } + @Override + public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) { + return myStructureDefinitions.get(theUrl); + } - @Override - public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { - return false; - } + @Override + public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { + return false; + } @Override public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) { @@ -188,8 +188,13 @@ public class PrePopulatedValidationSupport implements IValidationSupport { } @Override - public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { - return null; - } + public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { + return null; + } + + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/SnapshotGeneratingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/SnapshotGeneratingValidationSupport.java index e81b5d2f735..7b86424eaca 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/SnapshotGeneratingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/SnapshotGeneratingValidationSupport.java @@ -96,6 +96,11 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + return null; + } + private class MyProfileKnowledgeWorker implements ProfileUtilities.ProfileKnowledgeProvider { @Override public boolean isDatatype(String typeSimple) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java index f5ab6b533fa..14eb456a646 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java @@ -159,6 +159,16 @@ public class ValidationSupportChain implements IValidationSupport { return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); } + @Override + public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) { + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theContext, theSystem)) { + return next.lookupCode(theContext, theSystem, theCode); + } + } + return null; + } + @Override public List fetchAllStructureDefinitions(FhirContext theContext) { ArrayList retVal = new ArrayList<>(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 245bcdf6a0a..ef0124f5dde 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -52,7 +52,15 @@ the user has not explicitly configured a preference. ]]> - + + Breaking Change: + 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). + ]]> + + A new interceptor called ConsentInterceptor
]]> has been added. This interceptor allows JPA based servers to make appropriate consent decisions related to resources that @@ -340,6 +348,12 @@ a narrative on an untitled DiagnosticReport were fixed. Thanks to GitHub user @navyflower for reporting! + + A new attribute has been added to the @Operation annotation called + typeName
]]>. 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. +