Add ValueSet expansion support, fix profile bugs
HapiWorkerContext needed ValueSet expansion support for validation, now that resources are found and loaded. ParserBase had a bug when comparing names (was not looking at IdPart) Profiles had errors in FHIRPaths using invalid $context name
This commit is contained in:
parent
3623bbca16
commit
884bfbeed5
|
@ -31,13 +31,18 @@ import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptReferenceComponent;
|
||||||
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
|
import org.hl7.fhir.dstu2016may.model.ValueSet.ConceptSetComponent;
|
||||||
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
|
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent;
|
import org.hl7.fhir.dstu2016may.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||||
|
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander;
|
||||||
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||||
|
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpanderFactory;
|
||||||
|
import org.hl7.fhir.dstu2016may.terminologies.ValueSetExpanderSimple;
|
||||||
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
|
import org.hl7.fhir.dstu2016may.utils.IWorkerContext;
|
||||||
import org.hl7.fhir.dstu2016may.validation.IResourceValidator;
|
import org.hl7.fhir.dstu2016may.validation.IResourceValidator;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
public final class HapiWorkerContext implements IWorkerContext {
|
public final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory {
|
||||||
private final FhirContext myCtx;
|
private final FhirContext myCtx;
|
||||||
private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>();
|
private Map<String, Resource> myFetchedResourceCache = new HashMap<String, Resource>();
|
||||||
private IValidationSupport myValidationSupport;
|
private IValidationSupport myValidationSupport;
|
||||||
|
@ -259,10 +264,27 @@ public final class HapiWorkerContext implements IWorkerContext {
|
||||||
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
|
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueSetExpander getExpander() {
|
||||||
|
ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) {
|
public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk) {
|
||||||
throw new UnsupportedOperationException();
|
ValueSetExpansionOutcome vso;
|
||||||
|
try {
|
||||||
|
vso = getExpander().expand(theSource);
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
}
|
||||||
|
if (vso.getError() != null) {
|
||||||
|
throw new InvalidRequestException(vso.getError());
|
||||||
|
} else {
|
||||||
|
return vso;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -74,7 +74,7 @@ public abstract class ParserBase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (StructureDefinition sd : context.allStructures()) {
|
for (StructureDefinition sd : context.allStructures()) {
|
||||||
if (name.equals(sd.getId())) {
|
if (name.equals(sd.getIdElement().getIdPart())) {
|
||||||
if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
|
if((ns == null || ns.equals(FormatUtilities.FHIR_NS)) && !ToolingExtensions.hasExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"))
|
||||||
return sd;
|
return sd;
|
||||||
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
|
String sns = ToolingExtensions.readStringExtension(sd, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace");
|
||||||
|
@ -92,7 +92,7 @@ public abstract class ParserBase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (StructureDefinition sd : context.allStructures()) {
|
for (StructureDefinition sd : context.allStructures()) {
|
||||||
if (name.equals(sd.getId())) {
|
if (name.equals(sd.getIdElement().getIdPart())) {
|
||||||
return sd;
|
return sd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20460,7 +20460,7 @@
|
||||||
<key value="dom-3"/>
|
<key value="dom-3"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource"/>
|
<human value="If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource"/>
|
||||||
<expression value="contained.select(('#'+id in $context.descendents().reference).not()).empty()"/>
|
<expression value="contained.select(('#'+id in %resource.descendents().reference).not()).empty()"/>
|
||||||
<xpath value="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))"/>
|
<xpath value="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<constraint>
|
<constraint>
|
||||||
|
@ -20649,7 +20649,7 @@
|
||||||
<key value="dom-3"/>
|
<key value="dom-3"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource"/>
|
<human value="If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource"/>
|
||||||
<expression value="contained.select(('#'+id in $context.descendents().reference).not()).empty()"/>
|
<expression value="contained.select(('#'+id in %resource.descendents().reference).not()).empty()"/>
|
||||||
<xpath value="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))"/>
|
<xpath value="not(exists(for $id in f:contained/*/@id return $id[not(ancestor::f:contained/parent::*/descendant::f:reference/@value=concat('#', $id))]))"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<constraint>
|
<constraint>
|
||||||
|
@ -121071,7 +121071,7 @@
|
||||||
<key value="obs-7"/>
|
<key value="obs-7"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="Component code SHALL not be same as observation code"/>
|
<human value="Component code SHALL not be same as observation code"/>
|
||||||
<expression value="component.where(code = $context.code).empty()"/>
|
<expression value="component.where(code = %resource.code).empty()"/>
|
||||||
<xpath value="not(exists(f:component/f:code)) or count(for $coding in f:code/f:coding return parent::*/f:component/f:code/f:coding[f:code/@value=$coding/f:code/@value and f:system/@value=$coding/f:system/@value])=0"/>
|
<xpath value="not(exists(f:component/f:code)) or count(for $coding in f:code/f:coding return parent::*/f:component/f:code/f:coding[f:code/@value=$coding/f:code/@value and f:system/@value=$coding/f:system/@value])=0"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<mapping>
|
<mapping>
|
||||||
|
@ -122307,7 +122307,7 @@
|
||||||
<key value="obs-7"/>
|
<key value="obs-7"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="Component code SHALL not be same as observation code"/>
|
<human value="Component code SHALL not be same as observation code"/>
|
||||||
<expression value="component.where(code = $context.code).empty()"/>
|
<expression value="component.where(code = %resource.code).empty()"/>
|
||||||
<xpath value="not(exists(f:component/f:code)) or count(for $coding in f:code/f:coding return parent::*/f:component/f:code/f:coding[f:code/@value=$coding/f:code/@value and f:system/@value=$coding/f:system/@value])=0"/>
|
<xpath value="not(exists(f:component/f:code)) or count(for $coding in f:code/f:coding return parent::*/f:component/f:code/f:coding[f:code/@value=$coding/f:code/@value and f:system/@value=$coding/f:system/@value])=0"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<mapping>
|
<mapping>
|
||||||
|
@ -154515,7 +154515,7 @@
|
||||||
<key value="sdf-8"/>
|
<key value="sdf-8"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="In any snapshot or differential, all the elements except the first have to have a path that starts with the path of the first + ".""/>
|
<human value="In any snapshot or differential, all the elements except the first have to have a path that starts with the path of the first + ".""/>
|
||||||
<expression value="snapshot.element.tail().all(path.startsWith($context.snapshot.element.first().path+'.')) and differential.element.tail().all(path.startsWith($context.differential.element.first().path+'.'))"/>
|
<expression value="snapshot.element.tail().all(path.startsWith(%resource.snapshot.element.first().path+'.')) and differential.element.tail().all(path.startsWith(%resource.differential.element.first().path+'.'))"/>
|
||||||
<xpath value="string-join(for $elementName in f:*[self::f:snapshot or self::f:differential]/f:element[position()>1]/f:path/@value return if (starts-with($elementName, concat($elementName/ancestor::f:element/parent::f:*/f:element[1]/f:path/@value, '.'))) then '' else $elementName,'')=''"/>
|
<xpath value="string-join(for $elementName in f:*[self::f:snapshot or self::f:differential]/f:element[position()>1]/f:path/@value return if (starts-with($elementName, concat($elementName/ancestor::f:element/parent::f:*/f:element[1]/f:path/@value, '.'))) then '' else $elementName,'')=''"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<constraint>
|
<constraint>
|
||||||
|
@ -155580,7 +155580,7 @@
|
||||||
<key value="sdf-8"/>
|
<key value="sdf-8"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="In any snapshot or differential, all the elements except the first have to have a path that starts with the path of the first + ".""/>
|
<human value="In any snapshot or differential, all the elements except the first have to have a path that starts with the path of the first + ".""/>
|
||||||
<expression value="snapshot.element.tail().all(path.startsWith($context.snapshot.element.first().path+'.')) and differential.element.tail().all(path.startsWith($context.differential.element.first().path+'.'))"/>
|
<expression value="snapshot.element.tail().all(path.startsWith(%resource.snapshot.element.first().path+'.')) and differential.element.tail().all(path.startsWith(%resource.differential.element.first().path+'.'))"/>
|
||||||
<xpath value="string-join(for $elementName in f:*[self::f:snapshot or self::f:differential]/f:element[position()>1]/f:path/@value return if (starts-with($elementName, concat($elementName/ancestor::f:element/parent::f:*/f:element[1]/f:path/@value, '.'))) then '' else $elementName,'')=''"/>
|
<xpath value="string-join(for $elementName in f:*[self::f:snapshot or self::f:differential]/f:element[position()>1]/f:path/@value return if (starts-with($elementName, concat($elementName/ancestor::f:element/parent::f:*/f:element[1]/f:path/@value, '.'))) then '' else $elementName,'')=''"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<constraint>
|
<constraint>
|
||||||
|
|
|
@ -4681,7 +4681,7 @@
|
||||||
<key value="ref-1"/>
|
<key value="ref-1"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="SHALL have a local reference if the resource is provided inline"/>
|
<human value="SHALL have a local reference if the resource is provided inline"/>
|
||||||
<expression value="reference.startsWith('#').not() or ($context.reference.substring(1).trace('url') in $resource.contained.id.trace('ids'))"/>
|
<expression value="reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))"/>
|
||||||
<xpath value="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])"/>
|
<xpath value="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<mapping>
|
<mapping>
|
||||||
|
@ -4779,7 +4779,7 @@
|
||||||
<key value="ref-1"/>
|
<key value="ref-1"/>
|
||||||
<severity value="error"/>
|
<severity value="error"/>
|
||||||
<human value="SHALL have a local reference if the resource is provided inline"/>
|
<human value="SHALL have a local reference if the resource is provided inline"/>
|
||||||
<expression value="reference.startsWith('#').not() or ($context.reference.substring(1).trace('url') in $resource.contained.id.trace('ids'))"/>
|
<expression value="reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))"/>
|
||||||
<xpath value="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])"/>
|
<xpath value="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])"/>
|
||||||
</constraint>
|
</constraint>
|
||||||
<mapping>
|
<mapping>
|
||||||
|
|
Loading…
Reference in New Issue