Merge pull request #1358 from hapifhir/2023-07-gg-fix-fhirpath-r3

2023 07 gg fix fhirpath r3
This commit is contained in:
Grahame Grieve 2023-07-22 21:44:52 +10:00 committed by GitHub
commit 804575fc77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 304 additions and 32 deletions

View File

@ -2242,15 +2242,29 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (Utilities.isAbsoluteUrl(typeName)) {
return fetchResource(StructureDefinition.class, typeName);
} else {
return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+typeName);
Set<StructureDefinition> types = new HashSet<>();
types.addAll(fetchTypeDefinitions(typeName));
types.removeIf(sd -> sd.getDerivation() == TypeDerivationRule.CONSTRAINT);
if (types.size() == 0) {
return null; // throw new FHIRException("Unresolved type "+typeName+" (0)");
} else if (types.size() == 1) {
return types.iterator().next();
} else {
types.removeIf(sd -> !sd.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition/"));
if (types.size() != 1) {
throw new FHIRException("Ambiguous type "+typeName+" ("+types.toString()+") (contact Grahame Grieve for investigation)");
} else {
return types.iterator().next();
}
}
}
}
@Override
public List<StructureDefinition> fetchTypeDefinitions(String typeName) {
List<StructureDefinition> res = new ArrayList<>();
structures.listAll(res);
res.removeIf(sd -> !sd.hasType() || !sd.getType().equals(typeName));
res.removeIf(sd -> !sd.hasType() || !(sd.getType().equals(typeName) || sd.getTypeTail().equals(typeName)));
return res;
}

View File

@ -32,6 +32,7 @@ import org.hl7.fhir.r5.model.NamingSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
@ -156,8 +157,10 @@ public class ContextUtilities implements ProfileKnowledgeProvider {
public Set<String> getTypeNameSet() {
Set<String> result = new HashSet<String>();
for (StructureDefinition sd : context.fetchResourcesByType(StructureDefinition.class)) {
if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION)
if (sd.getKind() != StructureDefinitionKind.LOGICAL && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION &&
VersionUtilities.versionsCompatible(context.getVersion(), sd.getFhirVersion().toCode())) {
result.add(sd.getName());
}
}
return result;
}

View File

@ -249,6 +249,9 @@ public class TypeDetails {
return true;
if (tail != null && typesContains(sd.getUrl()+"#"+sd.getType()+tail))
return true;
if ("http://hl7.org/fhir/StructureDefinition/string".equals(sd.getUrl()) && typesContains(FP_String)) {
return true; // this is work around for R3
}
if (sd.hasBaseDefinition()) {
if (sd.getType().equals("uri"))
sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/string");

View File

@ -675,7 +675,7 @@ public class FHIRPathEngine {
if (ed.fixedType != null) {
types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType);
} else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) {
types = new TypeDetails(CollectionStatus.SINGLETON, ctxt+"#"+t);
types = new TypeDetails(CollectionStatus.SINGLETON, sd.getType()+"#"+t);
} else {
types = new TypeDetails(CollectionStatus.SINGLETON);
for (TypeRefComponent tt : ed.getDefinition().getType()) {

View File

@ -0,0 +1,43 @@
package org.hl7.fhir.utilities;
/**
* A few places in the code, we call System.exit(int) to pass on the exit code to
* other tools (for integration in scripts)
*
* But you don't want to do that while running the failing things under JUnit
* This class does two things
*
* * Remember the exit code while shutdown / cleanup happens
* * Allow for a test case to turn exiting off altogther
*
* @author grahamegrieve
*
*/
public class SystemExitManager {
private static int error;
private static boolean noExit;
public static int getError() {
return error;
}
public static void setError(int error) {
SystemExitManager.error = error;
}
public static boolean isNoExit() {
return noExit;
}
public static void setNoExit(boolean noExit) {
SystemExitManager.noExit = noExit;
}
public static void finish() {
if (!noExit && error > 0) {
System.exit(error);
}
}
}

View File

@ -922,6 +922,8 @@ public class I18nConstants {
public static final String TERMINOLOGY_TX_HINT = "TERMINOLOGY_TX_HINT";
public static final String TERMINOLOGY_TX_WARNING = "TERMINOLOGY_TX_WARNING";
public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
public static final String VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = "VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED";
public static final String VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = "VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED";
}

View File

@ -977,3 +977,5 @@ TERMINOLOGY_TX_HINT = {1}
TERMINOLOGY_TX_WARNING = {1}
SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED = This include has some concepts with displays and some without - check that this is what is intended
VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED = This SNOMED-CT based include has some concepts with semantic tags (FSN terms) and some without (preferred terms) - check that this is what is intended

View File

@ -594,7 +594,9 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
try {
loader.load(ref.getCnt());
} catch (Throwable t) {
System.out.println(t.getMessage());
if (debug) {
System.out.println("Error during round 1 scanning: "+t.getMessage());
}
}
}
}

View File

@ -67,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE.
import org.apache.commons.text.WordUtils;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.utilities.FileFormat;
import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
@ -163,7 +164,8 @@ public class ValidatorCli {
});
} else {
displayHelpForDefaultTask();
}return;
}
return;
}
readParamsAndExecuteTask(tt, tts, cliContext, args);
@ -310,6 +312,7 @@ public class ValidatorCli {
}
System.out.println("Done. " + tt.report()+". Max Memory = "+Utilities.describeSize(Runtime.getRuntime().maxMemory()));
SystemExitManager.finish();
}
private CliTask selectCliTask(CliContext cliContext, String[] params) {

View File

@ -48,6 +48,7 @@ import org.hl7.fhir.r5.renderers.spreadsheets.ValueSetSpreadsheetGenerator;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities;
@ -225,7 +226,9 @@ public class ValidationService {
Thread.sleep(watchScanDelay);
}
} while (watch != ValidatorWatchMode.NONE);
System.exit(ec > 0 ? 1 : 0);
if (ec > 0) {
SystemExitManager.setError(1);
}
}
private int countErrors(OperationOutcome oo) {

View File

@ -1,5 +1,6 @@
package org.hl7.fhir.validation.cli.tasks;
import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.Params;
@ -43,6 +44,7 @@ public class TxTestsTask extends StandaloneTask{
final String tx = Params.getParam(args, Params.TERMINOLOGY);
final String filter = Params.getParam(args, Params.FILTER);
boolean ok = new TxTester(new TxTester.InternalTxLoader(source, output), tx, false).setOutput(output).execute(version, filter);
System.exit(ok ? 1 : 0);
SystemExitManager.setError(ok ? 1 : 0);
SystemExitManager.finish();
}
}

View File

@ -471,12 +471,14 @@ public class StructureDefinitionValidator extends BaseValidator {
}
// if we see fixed[x] or pattern[x] applied to a repeating element, we'll give the user a hint
}
List<Element> constraints = element.getChildrenByName("constraint");
int cc = 0;
for (Element invariant : constraints) {
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl) && ok;
cc++;
}
if (snapshot) { // we just don't have enough information to figure out the context in a differential
List<Element> constraints = element.getChildrenByName("constraint");
int cc = 0;
for (Element invariant : constraints) {
ok = validateElementDefinitionInvariant(errors, invariant, stack.push(invariant, cc, null, null), invariantMap, elements, element, element.getNamedChildValue("path"), rootPath, profileUrl) && ok;
cc++;
}
}
return ok;
}
@ -504,7 +506,7 @@ public class StructureDefinitionValidator extends BaseValidator {
Element oldte = te;
te = getParent(elements, te);
if (te != null) {
exp = tail(oldte, te)+"."+exp;
exp = tail(oldte, te)+".all("+exp+")";
types = getTypesForElement(elements, te);
}
}
@ -547,13 +549,17 @@ public class StructureDefinitionValidator extends BaseValidator {
private List<String> getTypesForElement(List<Element> elements, Element element) {
List<String> types = new ArrayList<>();
for (Element tr : element.getChildrenByName("type")) {
String t = tr.getNamedChildValue("code");
if (t != null) {
if (isAbstractType(t) && hasChildren(element, elements) ) {
types.add(element.getNamedChildValue("path"));
} else {
types.add(t);
if (element.hasChild("path") && !element.getNamedChildValue("path").contains(".")) {
types.add(element.getNamedChildValue("path"));
} else {
for (Element tr : element.getChildrenByName("type")) {
String t = tr.getNamedChildValue("code");
if (t != null) {
if (isAbstractType(t) && hasChildren(element, elements) ) {
types.add(element.getNamedChildValue("path"));
} else {
types.add(t);
}
}
}
}

View File

@ -22,10 +22,79 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.hl7.fhir.validation.BaseValidator;
import org.hl7.fhir.validation.TimeTracker;
import org.hl7.fhir.validation.instance.InstanceValidator;
import org.hl7.fhir.validation.instance.type.ValueSetValidator.SystemLevelValidator;
import org.hl7.fhir.validation.instance.utils.NodeStack;
public class ValueSetValidator extends BaseValidator {
public class SystemLevelValidator {
protected List<ValidationMessage> errors;
protected Element inc;
protected NodeStack stack;
private boolean noDisplay = false;
private boolean hasDisplay = false;
protected SystemLevelValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
super();
this.errors = errors;
this.inc = inc;
this.stack = stack;
}
public void checkConcept(String code, String display) {
if (Utilities.noString(display)) {
noDisplay = true;
} else {
hasDisplay = true;
}
}
public void finish() {
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noDisplay && hasDisplay), I18nConstants.VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED);
}
}
public class SnomedCTValidator extends SystemLevelValidator {
private boolean noTag = false;
private boolean hasTag = false;
protected SnomedCTValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
super(errors, inc, stack);
}
public void checkConcept(String code, String display) {
super.checkConcept(code, display);
if (!Utilities.noString(display)) {
boolean tagged = display.endsWith(")") && display.indexOf("(") > display.length() - 20;
if (tagged) {
hasTag = true;
} else {
noTag = true;
}
}
}
public void finish() {
hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED);
}
}
public class GeneralValidator extends SystemLevelValidator {
protected GeneralValidator(List<ValidationMessage> errors, Element inc, NodeStack stack) {
super(errors, inc, stack);
}
}
private SystemLevelValidator getSystemValidator(String system, List<ValidationMessage> errors, Element inc, NodeStack stack) {
if (system == null) {
return new GeneralValidator(errors, inc, stack);
}
switch (system) {
case "http://snomed.info/sct" :return new SnomedCTValidator(errors, inc, stack);
default: return new GeneralValidator(errors, inc, stack);
}
}
public class VSCodingValidationRequest extends CodingValidationRequest {
private NodeStack stack;
@ -139,6 +208,8 @@ public class ValueSetValidator extends BaseValidator {
}
List<Element> concepts = include.getChildrenByName("concept");
List<Element> filters = include.getChildrenByName("filter");
SystemLevelValidator slv = getSystemValidator(system, errors, include, stack);
if (!Utilities.noString(system)) {
boolean systemOk = true;
int cc = 0;
@ -147,10 +218,10 @@ public class ValueSetValidator extends BaseValidator {
for (Element concept : concepts) {
// we treat the first differently because we want to know if tbe system is worth validating. if it is, then we batch the rest
if (first) {
systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version);
systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version, slv);
first = false;
} else if (systemOk) {
batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version));
batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version, slv));
}
cc++;
}
@ -180,19 +251,24 @@ public class ValueSetValidator extends BaseValidator {
int cf = 0;
for (Element filter : filters) {
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) {
if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version, slv)) {
systemOk = false;
}
cf++;
}
slv.finish();
} else {
warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING);
}
return ok;
}
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version) {
private boolean validateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stackInc, NodeStack stack, String system, String version, SystemLevelValidator slv) {
String code = concept.getChildValue("code");
String display = concept.getChildValue("display");
slv.checkConcept(code, display);
if (version == null) {
ValidationResult vv = context.validateCode(ValidationOptions.defaults(), new Coding(system, code, null), null);
if (vv.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
@ -229,8 +305,11 @@ public class ValueSetValidator extends BaseValidator {
return true;
}
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version) {
private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List<ValidationMessage> errors, Element concept, NodeStack stack, String system, String version, SystemLevelValidator slv) {
String code = concept.getChildValue("code");
String display = concept.getChildValue("display");
slv.checkConcept(code, display);
Coding c = new Coding(system, code, null);
if (version != null) {
c.setVersion(version);
@ -238,7 +317,11 @@ public class ValueSetValidator extends BaseValidator {
return new VSCodingValidationRequest(stack, c);
}
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element include, NodeStack push, String system, String version) {
private boolean validateValueSetIncludeFilter(List<ValidationMessage> errors, Element filter, NodeStack push, String system, String version, SystemLevelValidator slv) {
//
// String display = concept.getChildValue("display");
// slv.checkConcept(code, display);
return true;
}
}

View File

@ -32,6 +32,7 @@ import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeableConcept;
@ -290,7 +291,7 @@ public class TerminologyServiceTests {
}
if (vm.getUnknownSystems() != null) {
for (String s : vm.getUnknownSystems()) {
res.addParameter("x-caused-by-unknown-system", new UriType(s));
res.addParameter("x-caused-by-unknown-system", new CanonicalType(s));
}
}
if (vm.getIssues().size() > 0) {

View File

@ -2,18 +2,22 @@ package org.hl7.fhir.validation.tests;
import org.hl7.fhir.r4.context.SimpleWorkerContext;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.SystemExitManager;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.validation.ValidatorCli;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled
public class CDAValidationTest {
private SimpleWorkerContext context;
@Test
public void test() throws Exception {
ValidatorCli.main(new String[] {TestingUtilities.loadTestResource("ccda.xml"), "-ig", "hl7.fhir.cda"});
String fn = TestingUtilities.tempFile("cda", "cda.xml");
TextFile.stringToFile(TestingUtilities.loadTestResource("cda/cda-original.xml"), fn);
SystemExitManager.setNoExit(true);
ValidatorCli.main(new String[] {fn, "-ig", "hl7.cda.uv.core#current"});
}
}

View File

@ -126,3 +126,20 @@ v: {
"system" : "urn:ietf:bcp:47"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
"code" : "001",
"display" : "World"
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "World",
"code" : "001",
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,33 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
"code" : "001"
}, "url": "http://hl7.org/fhir/ValueSet/jurisdiction--2", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "World",
"code" : "001",
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm",
"code" : "001"
}, "valueSet" :null, "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "World",
"code" : "001",
"system" : "http://unstats.un.org/unsd/methods/m49/m49.htm"
}
-------------------------------------------------------------------------------------

View File

@ -822,3 +822,54 @@ v: {
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "230993007"
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Worsening",
"code" : "230993007",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "385633008"
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Improving (qualifier value)",
"code" : "385633008",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "260388006"
}, "valueSet" :null, "langs":"[en-US, en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "No status change",
"code" : "260388006",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------