Additional fixes

This commit is contained in:
Lloyd McKenzie 2024-09-17 14:48:37 -06:00
parent db718631eb
commit 4810ed3e0e
2 changed files with 96 additions and 17 deletions

View File

@ -53,7 +53,7 @@ public class CapabilityStatementUtilities {
importedUrls.put(targetCS.getUrl(), importConformance); importedUrls.put(targetCS.getUrl(), importConformance);
CapabilityStatement mergedImportedCS = resolveImports(importedCS, importedUrls, importConformance); CapabilityStatement mergedImportedCS = resolveImports(importedCS, importedUrls, importConformance);
mergeCS(targetCS, mergedImportedCS, importConformance); mergeCS(resolvedCS, mergedImportedCS, importConformance);
} }
return resolvedCS; return resolvedCS;
@ -74,14 +74,14 @@ public class CapabilityStatementUtilities {
throw new FHIRException("Unable to handle messaging repetitions greater than one for imported Capability Statement - use one repetition with multiple messaging.supportedMessage elements."); throw new FHIRException("Unable to handle messaging repetitions greater than one for imported Capability Statement - use one repetition with multiple messaging.supportedMessage elements.");
else if (!importedCS.hasMessaging()) { else if (!importedCS.hasMessaging()) {
// Do nothing // Do nothing
} else if (targetCS.hasMessaging()) } else if (!targetCS.hasMessaging())
targetCS.setMessaging(importedCS.getMessaging()); targetCS.setMessaging(importedCS.getMessaging());
else { else {
CapabilityStatement.CapabilityStatementMessagingComponent targetMessaging = targetCS.getMessaging().get(0); CapabilityStatement.CapabilityStatementMessagingComponent targetMessaging = targetCS.getMessaging().get(0);
CapabilityStatement.CapabilityStatementMessagingComponent importedMessaging = importedCS.getMessaging().get(0); CapabilityStatement.CapabilityStatementMessagingComponent importedMessaging = importedCS.getMessaging().get(0);
merge(targetMessaging.getReliableCacheElement(), importedMessaging.getReliableCacheElement(), maxConformance, "messaging.reliableCache"); merge(targetMessaging.getReliableCacheElement(), importedMessaging.getReliableCacheElement(), maxConformance, "messaging.reliableCache");
if (targetMessaging.hasEndpoint() || importedMessaging.hasEndpoint()) if (importedMessaging.hasEndpoint())
throw new FHIRException("Importing capability statements where the importing or imported statement asserts endpoints is not supported"); throw new FHIRException("Importing capability statements that assert endpoints is not supported");
merge(targetMessaging.getSupportedMessage(), importedMessaging.getSupportedMessage(), maxConformance, "messaging.supportedMessage"); merge(targetMessaging.getSupportedMessage(), importedMessaging.getSupportedMessage(), maxConformance, "messaging.supportedMessage");
} }
merge(targetCS.getMessaging(), importedCS.getMessaging(), maxConformance, "messaging"); merge(targetCS.getMessaging(), importedCS.getMessaging(), maxConformance, "messaging");
@ -144,7 +144,7 @@ public class CapabilityStatementUtilities {
// do nothing // do nothing
} else if (!targetType.hasDefinition()) } else if (!targetType.hasDefinition())
targetType.setDefinitionElement(importedType.getDefinitionElement()); targetType.setDefinitionElement(importedType.getDefinitionElement());
else if (targetType.getDefinition().equals(importedType.getDefinition())) else if (!targetType.getDefinition().equals(importedType.getDefinition()))
throw new FHIRException("Differing definitions for same operation " + localContext + " in imported IG. Importing:" + targetType.getDefinition() + "; imported:" + importedType.getDefinition()); throw new FHIRException("Differing definitions for same operation " + localContext + " in imported IG. Importing:" + targetType.getDefinition() + "; imported:" + importedType.getDefinition());
} }
@ -154,20 +154,19 @@ public class CapabilityStatementUtilities {
// do nothing // do nothing
} else if (!targetType.hasDefinition()) { } else if (!targetType.hasDefinition()) {
targetType.setDefinitionElement((CanonicalType)fixMax(importedType.getDefinitionElement(), maxConformance)); targetType.setDefinitionElement((CanonicalType)fixMax(importedType.getDefinitionElement(), maxConformance));
} else if (targetType.getDefinition().equals(importedType.getDefinition())) } else if (!targetType.getDefinition().equals(importedType.getDefinition()))
throw new FHIRException("Differing definitions for same Search parameter " + localContext + " in imported IG. Importing:" + targetType.getDefinition() + "; imported:" + importedType.getDefinition()); throw new FHIRException("Differing definitions for same Search parameter " + localContext + " in imported IG. Importing:" + targetType.getDefinition() + "; imported:" + importedType.getDefinition());
if (!importedType.hasType()) { if (!importedType.hasType()) {
// do nothing // do nothing
} else if (!targetType.hasType()) { } else if (!targetType.hasType()) {
// TODO maxConformance
targetType.setTypeElement((Enumeration<Enumerations.SearchParamType>)fixMax(importedType.getTypeElement(), maxConformance)); targetType.setTypeElement((Enumeration<Enumerations.SearchParamType>)fixMax(importedType.getTypeElement(), maxConformance));
} else if (targetType.getType().equals(importedType.getType())) } else if (!targetType.getType().equals(importedType.getType()))
throw new FHIRException("Differing search types for same Search parameter " + localContext + " in imported IG. Importing:" + targetType.getType() + "; imported:" + importedType.getType()); throw new FHIRException("Differing search types for same Search parameter " + localContext + " in imported IG. Importing:" + targetType.getType() + "; imported:" + importedType.getType());
} }
void mergeProperties(CapabilityStatement.CapabilityStatementMessagingComponent targetType, CapabilityStatement.CapabilityStatementMessagingComponent importedType, String maxConformance, String context) throws FHIRException { void mergeProperties(CapabilityStatement.CapabilityStatementMessagingComponent targetType, CapabilityStatement.CapabilityStatementMessagingComponent importedType, String maxConformance, String context) throws FHIRException {
if (targetType.hasEndpoint() || importedType.hasEndpoint()) { if (importedType.hasEndpoint()) {
throw new FHIRException("Importing CapabilityStatements "); throw new FHIRException("Cannot handle importing messaging with declared endpoints");
} }
targetType.setReliableCacheElement(merge(targetType.getReliableCacheElement(), importedType.getReliableCacheElement(), maxConformance, context + ".reliableCache")); targetType.setReliableCacheElement(merge(targetType.getReliableCacheElement(), importedType.getReliableCacheElement(), maxConformance, context + ".reliableCache"));
merge(targetType.getSupportedMessage(), importedType.getSupportedMessage(), maxConformance, context + ".reliableCache"); merge(targetType.getSupportedMessage(), importedType.getSupportedMessage(), maxConformance, context + ".reliableCache");
@ -187,6 +186,8 @@ public class CapabilityStatementUtilities {
boolean match; boolean match;
if (targetType instanceof PrimitiveType) if (targetType instanceof PrimitiveType)
match = importedType.toString().equals(targetType.toString()); match = importedType.toString().equals(targetType.toString());
else if (importedType instanceof CodeableConcept)
match = match((CodeableConcept)targetType,(CodeableConcept)importedType);
else if (importedType instanceof CapabilityStatement.CapabilityStatementRestComponent) else if (importedType instanceof CapabilityStatement.CapabilityStatementRestComponent)
match = ((CapabilityStatement.CapabilityStatementRestComponent)targetType).getMode().equals(((CapabilityStatement.CapabilityStatementRestComponent)importedType).getMode()); match = ((CapabilityStatement.CapabilityStatementRestComponent)targetType).getMode().equals(((CapabilityStatement.CapabilityStatementRestComponent)importedType).getMode());
else if (importedType instanceof CapabilityStatement.CapabilityStatementRestResourceComponent) else if (importedType instanceof CapabilityStatement.CapabilityStatementRestResourceComponent)
@ -197,9 +198,16 @@ public class CapabilityStatementUtilities {
match = ((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)targetType).getName().equals(((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)importedType).getName()); match = ((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)targetType).getName().equals(((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)importedType).getName());
else if (importedType instanceof CapabilityStatement.CapabilityStatementMessagingComponent) else if (importedType instanceof CapabilityStatement.CapabilityStatementMessagingComponent)
match = true; // We only work if there's only one messaging component in each match = true; // We only work if there's only one messaging component in each
else if (importedType instanceof CapabilityStatement.ResourceInteractionComponent)
match = ((CapabilityStatement.ResourceInteractionComponent)targetType).getCode().equals(((CapabilityStatement.ResourceInteractionComponent)importedType).getCode());
else if (importedType instanceof CapabilityStatement.SystemInteractionComponent)
match = ((CapabilityStatement.SystemInteractionComponent)targetType).getCode().equals(((CapabilityStatement.SystemInteractionComponent)importedType).getCode());
else if (importedType instanceof CapabilityStatement.CapabilityStatementDocumentComponent) else if (importedType instanceof CapabilityStatement.CapabilityStatementDocumentComponent)
match = ((CapabilityStatement.CapabilityStatementDocumentComponent)targetType).getMode().equals(((CapabilityStatement.CapabilityStatementDocumentComponent)importedType).getMode()) && match = ((CapabilityStatement.CapabilityStatementDocumentComponent)targetType).getMode().equals(((CapabilityStatement.CapabilityStatementDocumentComponent)importedType).getMode()) &&
((CapabilityStatement.CapabilityStatementDocumentComponent)targetType).getProfile().equals(((CapabilityStatement.CapabilityStatementDocumentComponent)importedType).getProfile()); ((CapabilityStatement.CapabilityStatementDocumentComponent)targetType).getProfile().equals(((CapabilityStatement.CapabilityStatementDocumentComponent)importedType).getProfile());
else if (importedType instanceof CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent)
match = ((CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent)targetType).getMode().equals(((CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent)importedType).getMode())
&& ((CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent)targetType).getDefinition().equals(((CapabilityStatement.CapabilityStatementMessagingSupportedMessageComponent)importedType).getDefinition());
else if (importedType instanceof Extension) { else if (importedType instanceof Extension) {
if (((Extension)importedType).getUrl().equals(ExtensionConstants.EXT_CSDECLARED_PROFILE)) if (((Extension)importedType).getUrl().equals(ExtensionConstants.EXT_CSDECLARED_PROFILE))
match = ((Extension)targetType).getValueCanonicalType().getValue().equals(((Extension)importedType).getValueCanonicalType().getValue()); match = ((Extension)targetType).getValueCanonicalType().getValue().equals(((Extension)importedType).getValueCanonicalType().getValue());
@ -227,7 +235,9 @@ public class CapabilityStatementUtilities {
mergeProperties((CapabilityStatement.CapabilityStatementRestResourceOperationComponent)foundType, (CapabilityStatement.CapabilityStatementRestResourceOperationComponent)importedType, context); mergeProperties((CapabilityStatement.CapabilityStatementRestResourceOperationComponent)foundType, (CapabilityStatement.CapabilityStatementRestResourceOperationComponent)importedType, context);
else if (importedType instanceof CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent) else if (importedType instanceof CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)
mergeProperties((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)foundType, (CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)importedType, maxConformance, context); mergeProperties((CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)foundType, (CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent)importedType, maxConformance, context);
else if (importedType instanceof CapabilityStatement.CapabilityStatementDocumentComponent) { else if (importedType instanceof CapabilityStatement.ResourceInteractionComponent || importedType instanceof CapabilityStatement.SystemInteractionComponent) {
// No properties to merge
} else if (importedType instanceof CapabilityStatement.CapabilityStatementDocumentComponent) {
// No properties to merge // No properties to merge
} else if (importedType instanceof CapabilityStatement.CapabilityStatementMessagingComponent) } else if (importedType instanceof CapabilityStatement.CapabilityStatementMessagingComponent)
mergeProperties((CapabilityStatement.CapabilityStatementMessagingComponent)foundType, (CapabilityStatement.CapabilityStatementMessagingComponent)importedType, maxConformance, context); mergeProperties((CapabilityStatement.CapabilityStatementMessagingComponent)foundType, (CapabilityStatement.CapabilityStatementMessagingComponent)importedType, maxConformance, context);
@ -239,11 +249,44 @@ public class CapabilityStatementUtilities {
} else if (((Extension) importedType).getUrl().equals(ExtensionConstants.EXT_CSSEARCH_PARAMETER_COMBINATION)) } else if (((Extension) importedType).getUrl().equals(ExtensionConstants.EXT_CSSEARCH_PARAMETER_COMBINATION))
mergeSearchComboExt(((Extension) foundType), ((Extension) importedType), context + ".extension(SearchCombo - " + requiredSort(importedType) + ")"); mergeSearchComboExt(((Extension) foundType), ((Extension) importedType), context + ".extension(SearchCombo - " + requiredSort(importedType) + ")");
} }
mergeExpectations((DataType) foundType, (DataType) importedType, maxConformance); mergeExpectations((Element) foundType, (Element) importedType, maxConformance);
} }
} }
} }
/*
* Two CodeableConcepts match if they have the same text and their codings match by code + system (and version if present)
*/
private boolean match(CodeableConcept a, CodeableConcept b) {
if (a.hasText() || b.hasText())
if (a.hasText()!= b.hasText() || !a.getText().equals(b.getText()))
return false;
if (a.getCoding().size()!= b.getCoding().size())
return false;
for (Coding codeA: a.getCoding()) {
boolean codingMatch = false;
for (Coding codeB: b.getCoding()) {
if (codeA.hasSystem() != codeB.hasSystem())
continue;
if (codeA.hasSystem() && !codeA.getSystem().equals(codeB.getSystem()))
continue;
if (codeA.hasCode() != codeB.hasCode())
continue;
if (codeA.hasCode() && !codeA.getCode().equals(codeB.getCode()))
continue;
if (codeA.hasVersion() != codeB.hasVersion())
continue;
if (codeA.hasVersion() && !codeA.getVersion().equals(codeB.getVersion()))
continue;
codingMatch = true;
break;
}
if (!codingMatch)
return false;
}
return true;
}
private List<String> extensionValueList(Extension sortExtension, String url) { private List<String> extensionValueList(Extension sortExtension, String url) {
List<String> aList = new ArrayList<>(); List<String> aList = new ArrayList<>();
for (Extension e: sortExtension.getExtensionsByUrl(url)) { for (Extension e: sortExtension.getExtensionsByUrl(url)) {
@ -402,9 +445,9 @@ public class CapabilityStatementUtilities {
* Selects whichever code exists if only one exists, otherwise checks that the two codes match and merges conformance expectations * Selects whichever code exists if only one exists, otherwise checks that the two codes match and merges conformance expectations
*/ */
protected BooleanType merge(BooleanType targetBool, BooleanType importedBool, String maxConformance, String context) throws FHIRException { protected BooleanType merge(BooleanType targetBool, BooleanType importedBool, String maxConformance, String context) throws FHIRException {
if (targetBool == null) if (targetBool == null || targetBool.getValue() == null)
return (BooleanType)fixMax(importedBool,maxConformance); return (BooleanType)fixMax(importedBool,maxConformance);
else if (importedBool == null) else if (importedBool == null || importedBool.getValue() == null)
return targetBool; return targetBool;
else if (targetBool.getValue().equals(importedBool.getValue())) { else if (targetBool.getValue().equals(importedBool.getValue())) {
mergeExpectations(targetBool, importedBool, maxConformance); mergeExpectations(targetBool, importedBool, maxConformance);
@ -428,8 +471,6 @@ public class CapabilityStatementUtilities {
public void mergeExpectations(Element target, Element source, String maxConformance) { public void mergeExpectations(Element target, Element source, String maxConformance) {
// Todo fix maxConformance
if (target.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) { if (target.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) {
Extension targetExpectation = target.getExtensionByUrl(ToolingExtensions.EXT_CAP_STMT_EXPECT); Extension targetExpectation = target.getExtensionByUrl(ToolingExtensions.EXT_CAP_STMT_EXPECT);
if (!targetExpectation.getValueCodeType().getCode().equals("SHALL") && source.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) { if (!targetExpectation.getValueCodeType().getCode().equals("SHALL") && source.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) {

View File

@ -1,15 +1,22 @@
package org.hl7.fhir.r5.utils; package org.hl7.fhir.r5.utils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.context.ContextUtilities; import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,12 +40,43 @@ class CapabilityStatementUtilitiesTests {
private static final Enumeration CR_FULLSUPPORT = (new CapabilityStatement.ConditionalReadStatusEnumFactory()).fromType(new CodeType("full-support")); private static final Enumeration CR_FULLSUPPORT = (new CapabilityStatement.ConditionalReadStatusEnumFactory()).fromType(new CodeType("full-support"));
private CapabilityStatementUtilities csu; private CapabilityStatementUtilities csu;
private IWorkerContext ctxt;
CapabilityStatementUtilitiesTests() { CapabilityStatementUtilitiesTests() {
IWorkerContext ctxt = TestingUtilities.getSharedWorkerContext(); ctxt = TestingUtilities.getSharedWorkerContext();
csu = new CapabilityStatementUtilities(ctxt); csu = new CapabilityStatementUtilities(ctxt);
} }
@Test
void testOverall() {
IParser p = new XmlParser(false);
CapabilityStatement c1 = null;
CapabilityStatement c2 = null;
CapabilityStatement expected = null;
try {
InputStream strm1 = TestingUtilities.loadTestResourceStream("r5", "capabilitystatement-import", "CapabilityStatement-1.xml");
InputStream strm2 = TestingUtilities.loadTestResourceStream("r5", "capabilitystatement-import", "CapabilityStatement-2.xml");
InputStream strm3 = TestingUtilities.loadTestResourceStream("r5", "capabilitystatement-import", "CapabilityStatement-2merged.xml");
c1 = (CapabilityStatement)p.parse(strm1);
c2 = (CapabilityStatement)p.parse(strm2);
expected = (CapabilityStatement)p.parse(strm3);
} catch (IOException e) {
}
if (c1==null || c2==null)
Assertions.fail("Unable to read source CapabilityStatements");
ctxt.cacheResource(c1);
CapabilityStatement out = csu.resolveImports(c2);
try {
String s1 = p.composeString(out);
String s2 = p.composeString(expected);
Assertions.assertEquals(s1, s2, "Merged capability statement must match expected value");
} catch (IOException e) {
Assertions.fail("Error serializing CapabilityStatements.");
}
}
@Test @Test
void testMergeList() { void testMergeList() {
List l1 = new ArrayList(); List l1 = new ArrayList();