Merge pull request #1268 from hapifhir/gg-202304-snapshot-fixes

Gg 202304 snapshot fixes
This commit is contained in:
Grahame Grieve 2023-05-22 19:09:45 +10:00 committed by GitHub
commit bd49a13d6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 885 additions and 123 deletions

View File

@ -1,7 +1,12 @@
## Validator Changes
* no changes
* Snapshot Generation Changes:
** Check for slicenames without any slicing
** Check for additional slicing rules in a set of slices
** Check that the minimum cardinality of a set of slices is correct
* Clean up duplicate errors when dates/dateTimes/instants have invalid formats
* Handle unknown code systems consistently when validating coded elements
## Other code changes
* no changes
* Add support for R4B to loader

View File

@ -75,6 +75,8 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader {
protected BaseLoaderR5 loaderFactory(NpmPackage npm) throws JsonSyntaxException, IOException {
if (VersionUtilities.isR5Plus(npm.fhirVersion())) {
return new R5ToR5Loader(types, lkp.forNewPackage(npm));
} else if (VersionUtilities.isR4BVer(npm.fhirVersion())) {
return new R4BToR5Loader(types, lkp.forNewPackage(npm), npm.version());
} else if (VersionUtilities.isR4Ver(npm.fhirVersion())) {
return new R4ToR5Loader(types, lkp.forNewPackage(npm), npm.version());
} else if (VersionUtilities.isR3Ver(npm.fhirVersion())) {

View File

@ -8,14 +8,20 @@ import java.util.Set;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.ElementRedirection;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionSlicingComponent;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionSnapshotComponent;
import org.hl7.fhir.r5.utils.ToolingExtensions;
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.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
@ -267,12 +273,28 @@ public class ProfilePathProcessor {
if (!diffMatches.get(0).hasSlicing())
outcome.setSlicing(profileUtilities.makeExtensionSlicing());
else
else {
outcome.setSlicing(diffMatches.get(0).getSlicing().copy());
for (int i = 1; i < diffMatches.size(); i++) {
if (diffMatches.get(i).hasSlicing()) {
if (!slicingMatches(diffMatches.get(0).getSlicing(), diffMatches.get(i).getSlicing())) {
profileUtilities.getMessages().add(new ValidationMessage(Source.InstanceValidator, ValidationMessage.IssueType.BUSINESSRULE, diffMatches.get(0).getPath(),
profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_CHANGE_SLICING, diffMatches.get(0).getId(), slicingSummary(diffMatches.get(0).getSlicing()), diffMatches.get(i).getId(), slicingSummary(diffMatches.get(i).getSlicing())),
ValidationMessage.IssueSeverity.ERROR));
} else {
profileUtilities.getMessages().add(new ValidationMessage(Source.InstanceValidator, ValidationMessage.IssueType.BUSINESSRULE, diffMatches.get(0).getPath(),
profileUtilities.getContext().formatMessage(I18nConstants.ATTEMPT_TO_CHANGE_SLICING, diffMatches.get(0).getId(), diffMatches.get(i).getId()),
IssueSeverity.INFORMATION));
}
}
}
}
if (cursors.resultPathBase != null) {
if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
}
debugCheck(outcome);
getResult().getElement().add(outcome);
slicerElement = outcome;
@ -337,6 +359,20 @@ public class ProfilePathProcessor {
cursors.diffCursor = newDiffLimit + 1;
}
private String slicingSummary(ElementDefinitionSlicingComponent s) {
return s.toString();
}
private boolean slicingMatches(ElementDefinitionSlicingComponent s1, ElementDefinitionSlicingComponent s2) {
if ((!s1.hasOrdered() && s2.hasOrdered()) || (s1.hasOrdered() && s2.hasOrdered() && !Base.compareDeep(s1.getOrderedElement(), s2.getOrderedElement(), false))) {
return false;
}
if ((!s1.hasRules() && s2.hasRules()) || (s1.hasRules() && s2.hasRules() && !Base.compareDeep(s1.getRulesElement(), s2.getRulesElement(), false))) {
return false;
}
return Base.compareDeep(s1.getDiscriminator(), s2.getDiscriminator(), false);
}
private void processSimplePathWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors) {
int start = 0;
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
@ -344,13 +380,14 @@ public class ProfilePathProcessor {
ElementDefinition elementToRemove = null;
boolean shortCut = !typeList.isEmpty() && typeList.get(0).getType() != null;
// we come here whether they are sliced in the diff, or whether the short cut is used.
String path = diffMatches.get(0).getPath();
if (shortCut) {
// this is the short cut method, we've just dived in and specified a type slice.
// in R3 (and unpatched R4, as a workaround right now...
if (!VersionUtilities.isR4Plus(profileUtilities.getContext().getVersion()) || !profileUtilities.isNewSlicingProcessing()) { // newSlicingProcessing is a work around for editorial loop dependency
// we insert a cloned element with the right types at the start of the diffMatches
ElementDefinition ed = new ElementDefinition();
ed.setPath(profileUtilities.determineTypeSlicePath(diffMatches.get(0).getPath(), currentBasePath));
ed.setPath(profileUtilities.determineTypeSlicePath(path, currentBasePath));
for (TypeSlice ts : typeList)
ed.addType().setCode(ts.getType());
ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent());
@ -365,7 +402,7 @@ public class ProfilePathProcessor {
// so the element we insert specifies no types (= all types) allowed in the base, not just the listed type.
// see also discussion here: https://chat.fhir.org/#narrow/stream/179177-conformance/topic/Slicing.20a.20non-repeating.20element
ElementDefinition ed = new ElementDefinition();
ed.setPath(profileUtilities.determineTypeSlicePath(diffMatches.get(0).getPath(), currentBasePath));
ed.setPath(profileUtilities.determineTypeSlicePath(path, currentBasePath));
ed.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent());
ed.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this");
ed.getSlicing().setRules(ElementDefinition.SlicingRules.CLOSED);
@ -374,6 +411,13 @@ public class ProfilePathProcessor {
getDifferential().getElement().add(newDiffCursor, ed);
elementToRemove = ed;
}
} else { // if it's not a short cut, then the path has to be correct
String t1 = currentBasePath.substring(currentBasePath.lastIndexOf(".")+1);
String t2 = path.substring(path.lastIndexOf(".")+1);
if (!t1.equals(t2)) {
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.ED_PATH_WRONG_TYPE_MATCH, path.replace(t2, t1), path));
}
}
int newDiffLimit = profileUtilities.findEndOfElement(getDifferential(), newDiffCursor);
// the first element is setting up the slicing
@ -429,7 +473,7 @@ public class ProfilePathProcessor {
.processPaths(new ProfilePathProcessorState(cursors.base, cursors.baseCursor, newDiffCursor,
cursors.contextName, cursors.resultPathBase));
if (elementDefinition == null)
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, diffMatches.get(0).getPath()));
throw new FHIRException(profileUtilities.getContext().formatMessage(I18nConstants.DID_NOT_FIND_TYPE_ROOT_, path));
// now set up slicing on the e (cause it was wiped by what we called.
elementDefinition.setSlicing(new ElementDefinition.ElementDefinitionSlicingComponent());
elementDefinition.getSlicing().addDiscriminator().setType(ElementDefinition.DiscriminatorType.TYPE).setPath("$this");
@ -607,6 +651,7 @@ public class ProfilePathProcessor {
cursors.resultPathBase = outcome.getPath();
else if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
debugCheck(outcome);
getResult().getElement().add(outcome);
cursors.baseCursor++;
cursors.diffCursor = getDifferential().getElement().indexOf(diffMatches.get(0)) + 1;
@ -731,6 +776,7 @@ public class ProfilePathProcessor {
cursors.resultPathBase = outcome.getPath();
else if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH__OUTCOMEGETPATH___RESULTPATHBASE__, outcome.getPath(), cursors.resultPathBase));
debugCheck(outcome);
getResult().getElement().add(outcome);
if (profileUtilities.hasInnerDiffMatches(getDifferential(), currentBasePath, cursors.diffCursor, getDiffLimit(), cursors.base.getElement(), true)) {
// well, the profile walks into this, so we need to as well
@ -756,6 +802,9 @@ public class ProfilePathProcessor {
}
}
}
if (!profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + ".")) {
cursors.diffCursor++;
}
int start = cursors.diffCursor;
while (getDifferential().getElement().size() > cursors.diffCursor && profileUtilities.pathStartsWith(getDifferential().getElement().get(cursors.diffCursor).getPath(), currentBasePath + "."))
cursors.diffCursor++;
@ -807,7 +856,7 @@ public class ProfilePathProcessor {
}
cursors.contextName = dt.getUrl();
if (getRedirector() == null || getRedirector().isEmpty()) {
this
.incrementDebugIndent()
.withBaseLimit(dt.getSnapshot().getElement().size() - 1)
@ -893,6 +942,7 @@ public class ProfilePathProcessor {
diffMatches.get(0).setUserData(profileUtilities.UD_GENERATED_IN_SNAPSHOT, outcome); // because of updateFromDefinition isn't called
}
debugCheck(outcome);
getResult().getElement().add(outcome);
if (!diffMatches.get(0).hasSliceName()) { // it's not real content, just the slice
@ -942,6 +992,8 @@ public class ProfilePathProcessor {
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
for (int i = cursors.baseCursor + 1; i <= newBaseLimit; i++) {
outcome = profileUtilities.updateURLs(getUrl(), getWebUrl(), cursors.base.getElement().get(i).copy());
outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource()));
debugCheck(outcome);
getResult().getElement().add(outcome);
}
}
@ -978,6 +1030,7 @@ public class ProfilePathProcessor {
cursors.diffCursor = newDiffLimit + 1;
diffpos++;
} else {
debugCheck(outcome);
getResult().getElement().add(outcome);
cursors.baseCursor++;
// just copy any children on the base
@ -988,6 +1041,7 @@ public class ProfilePathProcessor {
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
outcome.setUserData(profileUtilities.UD_BASE_PATH, outcome.getPath());
outcome.setUserData(profileUtilities.UD_BASE_MODEL, getSourceStructureDefinition().getUrl());
debugCheck(outcome);
getResult().getElement().add(outcome);
cursors.baseCursor++;
}
@ -1016,6 +1070,7 @@ public class ProfilePathProcessor {
outcome.setMin(0); // we're in a slice, so it's only a mandatory if it's explicitly marked so
if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
debugCheck(outcome);
getResult().getElement().add(outcome);
profileUtilities.updateFromDefinition(outcome, diffItem, getProfileName(), isTrimDifferential(), getUrl(), getSourceStructureDefinition(), getDerived());
profileUtilities.removeStatusExtensions(outcome);
@ -1079,6 +1134,12 @@ public class ProfilePathProcessor {
cursors.baseCursor++;
}
private void debugCheck(ElementDefinition outcome) {
if (outcome.getPath().startsWith("List.") && "http://nictiz.nl/fhir/StructureDefinition/Bundle-MedicationOverview".equals(url)) {
System.out.println("wrong!");
}
}
private void processPathWithSlicedBaseWhereDiffsConstrainTypes(String currentBasePath, List<ElementDefinition> diffMatches, List<TypeSlice> typeList, ProfilePathProcessorState cursors) {
int start = 0;
int newBaseLimit = profileUtilities.findEndOfElement(cursors.base, cursors.baseCursor);
@ -1252,6 +1313,7 @@ public class ProfilePathProcessor {
cursors.resultPathBase = outcome.getPath();
else if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH));
debugCheck(outcome);
getResult().getElement().add(outcome);
// the profile walks into this, so we need to as well
// did we implicitly step into a new type?
@ -1295,6 +1357,7 @@ public class ProfilePathProcessor {
outcome.setPath(profileUtilities.fixedPathDest(getContextPathTarget(), outcome.getPath(), getRedirector(), getContextPathSource()));
if (!outcome.getPath().startsWith(cursors.resultPathBase))
throw new DefinitionException(profileUtilities.getContext().formatMessage(I18nConstants.ADDING_WRONG_PATH_IN_PROFILE___VS_, getProfileName(), outcome.getPath(), cursors.resultPathBase));
debugCheck(outcome);
getResult().getElement().add(outcome); // so we just copy it in
outcome.setUserData(profileUtilities.UD_BASE_MODEL, getSourceStructureDefinition().getUrl());
outcome.setUserData(profileUtilities.UD_BASE_PATH, cursors.resultPathBase);

View File

@ -49,6 +49,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.ElementRedirection;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.AllowUnknownProfile;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.ElementDefinitionCounter;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
import org.hl7.fhir.r5.elementmodel.Property;
@ -56,6 +57,7 @@ import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBaseComponent;
@ -130,6 +132,33 @@ import org.hl7.fhir.utilities.xml.SchematronWriter.Section;
*/
public class ProfileUtilities extends TranslatingUtilities {
public class ElementDefinitionCounter {
int count = 0;
ElementDefinition focus;
public ElementDefinitionCounter(ElementDefinition ed) {
focus = ed;
}
public int update() {
if (count > focus.getMin()) {
int was = focus.getMin();
focus.setMin(count);
return was;
}
return -1;
}
public void count(ElementDefinition ed) {
count = count + ed.getMin();
}
public ElementDefinition getFocus() {
return focus;
}
}
public enum MappingMergeModeOption {
DUPLICATE, // if there's more than one mapping for the same URI, just keep them all
IGNORE, // if there's more than one, keep the first
@ -288,6 +317,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private Map<ElementDefinition, SourcedChildDefinitions> childMapCache = new HashMap<>();
private AllowUnknownProfile allowUnknownProfile = AllowUnknownProfile.ALL_TYPES;
private MappingMergeModeOption mappingMergeMode = MappingMergeModeOption.APPEND;
private boolean forPublication;
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp, FHIRPathEngine fpe) {
super();
@ -690,6 +720,49 @@ public class ProfileUtilities extends TranslatingUtilities {
}
}
}
// check slicing is ok while we're at it. and while we're doing this. update the minimum count if we need to
String tn = derived.getType();
if (tn.contains("/")) {
tn = tn.substring(tn.lastIndexOf("/")+1);
}
System.out.println("Check slicing for "+derived.getVersionedUrl());
Map<String, ElementDefinitionCounter> slices = new HashMap<>();
int i = 0;
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
if (ed.hasSlicing()) {
slices.put(ed.getPath(), new ElementDefinitionCounter(ed));
System.out.println("Entering slicing for "+ed.getPath()+" ["+i+"]");
} else {
Set<String> toRemove = new HashSet<>();
for (String s : slices.keySet()) {
if (Utilities.charCount(s, '.') >= Utilities.charCount(ed.getPath(), '.') && !s.equals(ed.getPath())) {
toRemove.add(s);
}
}
for (String s : toRemove) {
int was = slices.get(s).update();
if (was > -1) {
String msg = "The slice definition for "+slices.get(s).getFocus().getId()+" had a minimum of "+was+" but the slices added up to a minimum of "+slices.get(s).getFocus().getMin()+" so the value has been adjusted in the snapshot";
System.out.println(msg);
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, forPublication ? ValidationMessage.IssueSeverity.ERROR : ValidationMessage.IssueSeverity.INFORMATION));
}
System.out.println("Exiting slicing for "+s+" at "+ed.getPath()+" ["+i+"]");
slices.remove(s);
}
}
if (ed.getPath().contains(".") && !ed.getPath().startsWith(tn+".")) {
throw new Error("The element "+ed.getId()+" in the profile '"+derived.getVersionedUrl()+" (["+i+"]) doesn't have the right path (should start with "+tn+".");
}
if (ed.hasSliceName() && !slices.containsKey(ed.getPath())) {
String msg = "The element "+ed.getId()+" (["+i+"]) launches straight into slicing without the slicing being set up properly first";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
if (ed.hasSliceName() && slices.containsKey(ed.getPath())) {
slices.get(ed.getPath()).count(ed);
}
i++;
}
// last, check for wrong profiles or target profiles
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
for (TypeRefComponent t : ed.getType()) {
@ -3978,5 +4051,16 @@ public class ProfileUtilities extends TranslatingUtilities {
return defn.getIsModifier();
}
public boolean isForPublication() {
return forPublication;
}
public void setForPublication(boolean forPublication) {
this.forPublication = forPublication;
}
public List<ValidationMessage> getMessages() {
return messages;
}
}

View File

@ -943,6 +943,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (!t.hasResult()) {
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
ValidationResult res = vsc.validateCode("Coding", t.getCoding());
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
@ -1059,12 +1060,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
Set<String> unknownSystems = new HashSet<>();
String localError = null;
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt);
vsc.setUnknownSystems(unknownSystems);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
if (!ValueSetUtilities.isServerSide(code.getSystem())) {
res = vsc.validateCode(path, code);
if (txCache != null) {
@ -1084,7 +1088,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
if (localError != null && tcc.getClient() == null) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
if (unknownSystems.size() > 0) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
} else {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
}
}
if (!options.isUseServer()) {
return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localError), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);
@ -1204,11 +1212,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
codeSystemsUsed.add(c.getSystem());
}
}
Set<String> unknownSystems = new HashSet<>();
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
vsc.setUnknownSystems(unknownSystems);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
res = vsc.validateCode("CodeableConcept", code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
@ -1349,6 +1360,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
version = ((PrimitiveType<?>) p.getValue()).asStringValue();
} else if (p.getName().equals("code")) {
code = ((PrimitiveType<?>) p.getValue()).asStringValue();
} else if (p.getName().equals("x-caused-by-unknown-system")) {
err = TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED;
} else if (p.getName().equals("cause")) {
try {
IssueType it = IssueType.fromCode(((StringType) p.getValue()).getValue());

View File

@ -123,6 +123,7 @@ public interface IWorkerContext {
private String diagnostics;
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
private CodeableConcept codeableConcept;
private Set<String> unknownSystems;
@Override
public String toString() {
@ -239,7 +240,12 @@ public interface IWorkerContext {
this.message = message;
return this;
}
public ValidationResult addToMessage(String message) {
this.message = this.message == null ? message : this.message +"; "+ message;
return this;
}
public ValidationResult setErrorClass(TerminologyServiceErrorClass errorClass) {
this.errorClass = errorClass;
return this;
@ -288,7 +294,16 @@ public interface IWorkerContext {
public CodeableConcept getCodeableConcept() {
return codeableConcept;
}
public Set<String> getUnknownSystems() {
return unknownSystems;
}
public ValidationResult setUnknownSystems(Set<String> unknownSystems) {
this.unknownSystems = unknownSystems;
return this;
}
}
public class CodingValidationRequest {

View File

@ -113,6 +113,9 @@ public class Manager {
}
public static ParserBase makeParser(IWorkerContext context, FhirFormat format) {
if (format == null) {
throw new Error("Programming logic error: no format known");
}
switch (format) {
case JSON : return new JsonParser(context);
case XML : return new XmlParser(context);

View File

@ -1413,12 +1413,17 @@ public class ElementDefinition extends BackboneType implements ICompositeType {
, ordered, rules);
}
public String fhirType() {
return "ElementDefinition.slicing";
public String fhirType() {
return "ElementDefinition.slicing";
}
}
}
@Override
public String toString() {
return (ordered == null ? "??" : "true".equals(ordered.asStringValue()) ? "ordered" : "unordered")+"/"+
(rules == null ? "??" : rules.asStringValue())+" "+discriminator.toString();
}
}
@Block()
public static class ElementDefinitionSlicingDiscriminatorComponent extends Element implements IBaseDatatypeElement {
@ -1671,6 +1676,11 @@ public class ElementDefinition extends BackboneType implements ICompositeType {
}
@Override
public String toString() {
return (type == null ? "??" : type.getCode()) + "="+(path == null ? "??" : path.asStringValue());
}
}
@Block()

View File

@ -167,7 +167,7 @@ public class ElementWrappers {
Property family = b.getChildByName("family");
Property given = wrapped.getChildByName("given");
String s = given != null && given.hasValues() ? given.getValues().get(0).primitiveValue() : "";
if (family != null && family.hasValues())
if (family != null && family.hasValues() && family.getValues().get(0).primitiveValue() != null)
s = s + " " + family.getValues().get(0).primitiveValue().toUpperCase();
return s;
} else {

View File

@ -11,6 +11,13 @@ import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
public class ValidationProcessInfo {
private TerminologyServiceErrorClass err;
private List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
public ValidationProcessInfo() {
}
public ValidationProcessInfo(List<OperationOutcomeIssueComponent> issues) {
this.issues = issues;
}
public TerminologyServiceErrorClass getErr() {
return err;
}

View File

@ -100,6 +100,8 @@ public class ValueSetValidator {
private List<CodeSystem> localSystems = new ArrayList<>();
Parameters expansionProfile;
private TerminologyCapabilities txCaps;
private Set<String> unknownSystems;
private boolean throwToServer;
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, Parameters expansionProfile, TerminologyCapabilities txCaps) {
this.valueset = source;
@ -120,6 +122,22 @@ public class ValueSetValidator {
analyseValueSet();
}
public Set<String> getUnknownSystems() {
return unknownSystems;
}
public void setUnknownSystems(Set<String> unknownSystems) {
this.unknownSystems = unknownSystems;
}
public boolean isThrowToServer() {
return throwToServer;
}
public void setThrowToServer(boolean throwToServer) {
this.throwToServer = throwToServer;
}
private void analyseValueSet() {
if (localContext != null) {
if (valueset != null) {
@ -174,10 +192,16 @@ public class ValueSetValidator {
if (context.isNoTerminologyServer()) {
if (c.hasVersion()) {
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODESYSTEM_VERSION, c.getSystem(), c.getVersion() , resolveCodeSystemVersions(c.getSystem()).toString());
res = new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path+".coding["+i+"].system", msg));
if (valueSetDependsOn(c.getSystem(), c.getVersion())) {
unknownSystems.add(c.getSystem()+"|"+c.getVersion());
}
res = new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path+".coding["+i+"].system", msg)).setUnknownSystems(unknownSystems);
} else {
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, c.getSystem(), c.getVersion());
res = new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path+".coding["+i+"].system", msg));
if (valueSetDependsOn(c.getSystem(), null)) {
unknownSystems.add(c.getSystem());
}
res = new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path+".coding["+i+"].system", msg)).setUnknownSystems(unknownSystems);
}
} else {
res = context.validateCode(options.withNoClient(), c, null);
@ -191,8 +215,9 @@ public class ValueSetValidator {
}
}
Coding foundCoding = null;
String msg = null;
Boolean result = false;
if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
Boolean result = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(", ");
for (Coding c : code.getCoding()) {
@ -212,11 +237,11 @@ public class ValueSetValidator {
}
}
if (result == null) {
String msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.INVALID, path, msg));
msg = context.formatMessage(I18nConstants.UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.WARNING, unknownSystems.isEmpty() ? IssueType.INVALID : IssueType.NOTFOUND, path, msg));
} else if (!result) {
String msg = context.formatMessagePlural(code.getCoding().size(), I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
msg = context.formatMessagePlural(code.getCoding().size(), I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), b.toString());
info.getIssues().addAll(makeIssue(IssueSeverity.ERROR, unknownSystems.isEmpty() ? IssueType.INVALID : IssueType.NOTFOUND, path, msg));
}
}
if (info.hasErrors()) {
@ -229,8 +254,11 @@ public class ValueSetValidator {
res.setVersion(foundCoding.hasVersion() ? foundCoding.getVersion() : ((CodeSystem) foundCoding.getUserData("cs")).getVersion());
res.setDisplay(cd.getDisplay());
}
res.setUnknownSystems(unknownSystems);
res.addCodeableConcept(vcc);
return res;
} else if (result == null) {
return new ValidationResult(IssueSeverity.WARNING, info.summary(), info.getIssues());
} else if (foundCoding == null) {
return new ValidationResult(IssueSeverity.ERROR, "Internal Error that should not happen", makeIssue(IssueSeverity.FATAL, IssueType.EXCEPTION, path, "Internal Error that should not happen"));
} else if (info.getIssues().size() > 0) {
@ -245,6 +273,15 @@ public class ValueSetValidator {
}
}
private boolean valueSetDependsOn(String system, String version) {
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
if (system.equals(inc.getSystem()) && (version == null || inc.getVersion() == null || version.equals(inc.getVersion()))) {
return true;
}
}
return false;
}
private String getVersion(Coding c) {
if (c.hasVersion()) {
return c.getVersion();
@ -329,6 +366,7 @@ public class ValueSetValidator {
ValidationResult res = null;
boolean inExpansion = false;
boolean inInclude = false;
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
VersionInfo vi = new VersionInfo(this);
String system = code.hasSystem() ? code.getSystem() : getValueSetSystemOrNull();
@ -363,8 +401,10 @@ public class ValueSetValidator {
if (cs == null) {
if (wv == null) {
warningMessage = context.formatMessage(I18nConstants.UNKNOWN_CODESYSTEM, system);
unknownSystems.add(system);
} else {
warningMessage = context.formatMessage(I18nConstants.UNKNOWN_CODESYSTEM_VERSION, system, wv, resolveCodeSystemVersions(system).toString());
unknownSystems.add(system+"|"+wv);
}
if (!inExpansion) {
if (valueset != null && valueset.hasExpansion()) {
@ -372,18 +412,18 @@ public class ValueSetValidator {
valueset.getUrl(),
code.getSystem(),
code.getCode().toString());
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path, msg));
throw new VSCheckerException(msg, issues);
} else {
List<OperationOutcomeIssueComponent> issues = new ArrayList<>();
issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.NOTFOUND, path+".system", warningMessage));
res = new ValidationResult(IssueSeverity.WARNING, warningMessage, issues);
if (valueset == null) {
throw new VSCheckerException(warningMessage, issues);
} else {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), code.toString());
issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
throw new VSCheckerException(warningMessage+"; "+msg, issues);
// String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), code.toString());
// issues.addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
// we don't do this yet
// throw new VSCheckerException(warningMessage, issues);
}
}
}
@ -407,7 +447,7 @@ public class ValueSetValidator {
// we'll take it on faith
String disp = getPreferredDisplay(cc);
res = new ValidationResult(system, cs.getVersion(), new ConceptDefinitionComponent().setCode(cc.getCode()).setDisplay(disp), disp);
res.setMessage("Resolved system "+system+", but the definition is not complete, so assuming value set include is correct");
res.addToMessage("Resolved system "+system+", but the definition is not complete, so assuming value set include is correct");
return res;
}
}
@ -419,12 +459,14 @@ public class ValueSetValidator {
// we just take the value set as face value then
res = new ValidationResult(system, wv, new ConceptDefinitionComponent().setCode(code.getCode()).setDisplay(code.getDisplay()), code.getDisplay());
if (!preferServerSide(system)) {
res.setMessage("Code System unknown, so assuming value set expansion is correct ("+warningMessage+")");
res.addToMessage("Code System unknown, so assuming value set expansion is correct ("+warningMessage+")");
}
} else {
// well, we didn't find a code system - try the expansion?
// disabled waiting for discussion
throw new FHIRException("No try the server");
if (throwToServer) {
throw new FHIRException("No; try the server");
}
}
} else {
inExpansion = checkExpansion(code, vi);
@ -432,7 +474,7 @@ public class ValueSetValidator {
}
String wv = vi.getVersion(system, code.getVersion());
ValidationProcessInfo info = new ValidationProcessInfo();
ValidationProcessInfo info = new ValidationProcessInfo(issues);
// then, if we have a value set, we check it's in the value set
if (valueset != null && options.getValueSetMode() != ValueSetMode.NO_MEMBERSHIP_CHECK) {
@ -446,20 +488,24 @@ public class ValueSetValidator {
res.setErrorClass(info.getErr());
}
if (ok == null) {
res.setMessage("Unable to check whether code is in value set "+valueset.getUrl()+": "+info.summary()).setSeverity(IssueSeverity.WARNING);
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.EXCEPTION, path, res.getMessage()));
String m = "Unable to check whether the code is in the value set "+valueset.getVersionedUrl();
res.addToMessage(m);
res.getIssues().addAll(makeIssue(IssueSeverity.WARNING, IssueType.NOTFOUND, path, m));
res.setUnknownSystems(unknownSystems);
res.setSeverity(IssueSeverity.ERROR); // back patching for display logic issue
} else if (!inExpansion && !inInclude) {
if (!info.getIssues().isEmpty()) {
res.setMessage("Not in value set "+valueset.getUrl()+": "+info.summary()).setSeverity(IssueSeverity.ERROR);
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, res.getMessage()));
} else {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), code.toString());
res.setMessage(msg).setSeverity(IssueSeverity.ERROR);
// if (!info.getIssues().isEmpty()) {
// res.setMessage("Not in value set "+valueset.getUrl()+": "+info.summary()).setSeverity(IssueSeverity.ERROR);
// res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, res.getMessage()));
// } else
// {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), code.toString());
res.addToMessage(msg).setSeverity(IssueSeverity.ERROR);
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
res.setDefinition(null);
res.setSystem(null);
res.setDisplay(null);
}
// }
} else if (warningMessage!=null) {
String msg = context.formatMessage(I18nConstants.CODE_FOUND_IN_EXPANSION_HOWEVER_, warningMessage);
res = new ValidationResult(IssueSeverity.WARNING, msg, makeIssue(IssueSeverity.WARNING, IssueType.EXCEPTION, path, msg));
@ -472,7 +518,7 @@ public class ValueSetValidator {
}
}
} else if ((res != null && !res.isOk())) {
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getUrl(), code.toString());
String msg = context.formatMessagePlural(1, I18nConstants.NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_, valueset.getVersionedUrl(), code.toString());
res.setMessage(res.getMessage()+"; "+msg);
res.getIssues().addAll(makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path, msg));
}
@ -483,7 +529,7 @@ public class ValueSetValidator {
return res;
}
private static final HashSet<String> SERVER_SIDE_LIST = new HashSet<>(Arrays.asList("http://fdasis.nlm.nih.gov", "http://hl7.org/fhir/sid/ndc", "http://loinc.org", "http://snomed.info/sct", "http://unitsofmeasure.org",
private static final Set<String> SERVER_SIDE_LIST = new HashSet<>(Arrays.asList("http://fdasis.nlm.nih.gov", "http://hl7.org/fhir/sid/ndc", "http://loinc.org", "http://snomed.info/sct", "http://unitsofmeasure.org",
"http://unstats.un.org/unsd/methods/m49/m49.htm", "http://varnomen.hgvs.org", "http://www.nlm.nih.gov/research/umls/rxnorm", "https://www.usps.com/",
"urn:ietf:bcp:13","urn:ietf:bcp:47","urn:ietf:rfc:3986", "urn:iso:std:iso:3166","urn:iso:std:iso:4217", "urn:oid:1.2.36.1.2001.1005.17"));
@ -917,6 +963,7 @@ public class ValueSetValidator {
public Boolean codeInValueSet(String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
return codeInValueSet("code", system, version, code, info);
}
public Boolean codeInValueSet(String path, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
if (valueset == null) {
return false;
@ -991,30 +1038,42 @@ public class ValueSetValidator {
// ok, we need the code system
CodeSystem cs = resolveCodeSystem(system, version);
if (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT)) {
// make up a transient value set with
ValueSet vs = new ValueSet();
vs.setStatus(PublicationStatus.ACTIVE);
vs.setUrl(valueset.getUrl()+"--"+vsiIndex);
vs.setVersion(valueset.getVersion());
vs.getCompose().addInclude(vsi);
ValidationResult res = context.validateCode(options.withNoClient(), new Coding(system, code, null), vs);
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
if (info != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
// server didn't know the code system either - we'll take it face value
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system)));
for (ConceptReferenceComponent cc : vsi.getConcept()) {
if (cc.getCode().equals(code)) {
return true;
if (throwToServer) {
// make up a transient value set with
ValueSet vs = new ValueSet();
vs.setStatus(PublicationStatus.ACTIVE);
vs.setUrl(valueset.getUrl()+"--"+vsiIndex);
vs.setVersion(valueset.getVersion());
vs.getCompose().addInclude(vsi);
ValidationResult res = context.validateCode(options.withNoClient(), new Coding(system, code, null), vs);
if (res.getErrorClass() == TerminologyServiceErrorClass.UNKNOWN || res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED || res.getErrorClass() == TerminologyServiceErrorClass.VALUESET_UNSUPPORTED) {
if (info != null && res.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
// server didn't know the code system either - we'll take it face value
info.addIssue(makeIssue(IssueSeverity.WARNING, IssueType.UNKNOWN, path, context.formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system)));
for (ConceptReferenceComponent cc : vsi.getConcept()) {
if (cc.getCode().equals(code)) {
return true;
}
}
info.setErr(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
return null;
}
info.setErr(TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED);
return false;
}
return false;
if (res.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
throw new NoTerminologyServiceException();
}
return res.isOk();
} else {
if (unknownSystems != null) {
if (version == null) {
unknownSystems.add(system);
} else {
unknownSystems.add(system+"|"+version);
}
}
return null;
}
if (res.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) {
throw new NoTerminologyServiceException();
}
return res.isOk();
} else {
if (vsi.hasFilter()) {
ok = true;

View File

@ -100,7 +100,7 @@ public interface IResourceValidator {
void setNoUnicodeBiDiControlChars(boolean noUnicodeBiDiControlChars);
boolean isForPublication();
void setForPublication(boolean forPublication);
IResourceValidator setForPublication(boolean forPublication);
/**
* Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning

View File

@ -585,8 +585,8 @@ public class SnapShotGenerationTests {
boolean structureDefinitionEquality = t1.equalsDeep(t2);
if (!structureDefinitionEquality) {
System.out.println("Encountered unexpected diff in structure definition");
DiffUtils.testDiff(dst.getAbsolutePath(), actualFilePath);
System.out.println("Encountered unexpected change in diff in structure definition");
// DiffUtils.testDiff(dst.getAbsolutePath(), actualFilePath);
}
Assertions.assertTrue(structureDefinitionEquality, "Output does not match expected");
}

View File

@ -590,7 +590,7 @@ public class Utilities {
public static boolean isPlural(String word) {
word = word.toLowerCase();
if ("restricts".equals(word) || "contains".equals(word) || "data".equals(word) || "specimen".equals(word) || "replaces".equals(word) || "addresses".equals(word)
|| "supplementalData".equals(word) || "instantiates".equals(word) || "imports".equals(word))
|| "supplementalData".equals(word) || "instantiates".equals(word) || "imports".equals(word) || "covers".equals(word))
return false;
Inflector inf = new Inflector();
return !inf.singularize(word).equals(word);
@ -1966,4 +1966,8 @@ public class Utilities {
return Utilities.startsWithInList(s.replace("https://", "http://"), FhirSettings.getTxFhirProduction(), FhirSettings.getTxFhirDevelopment(), FhirSettings.getTxFhirLocal());
}
public static String[] splitLines(String txt) {
return txt.split("\\r?\\n|\\r");
}
}

View File

@ -866,6 +866,9 @@ public class I18nConstants {
public static final String SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = "SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION";
public static final String SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE = "SM_TARGET_TRANSFORM_OP_UNKNOWN_SOURCE";
public static final String SM_TARGET_TRANSFORM_OP_INVALID_TYPE = "SM_TARGET_TRANSFORM_OP_INVALID_TYPE";
public static final String ED_PATH_WRONG_TYPE_MATCH = "ED_PATH_WRONG_TYPE_MATCH";
public static final String ATTEMPT_TO_CHANGE_SLICING = "ATTEMPT_TO_CHANGE_SLICING";
public static final String REPEAT_SLICING_IGNORED = "REPEAT_SLICING_IGNORED";
}

View File

@ -672,7 +672,7 @@ RENDER_BUNDLE_DOCUMENT_CONTENT = Additional Document Content
RENDER_BUNDLE_HEADER_DOC_ENTRY_URD = {0}. {1} ({2}/{3})
RENDER_BUNDLE_HEADER_DOC_ENTRY_U = {0}. {1}
RENDER_BUNDLE_HEADER_DOC_ENTRY_RD = {0}. {2}/{3}
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = Unable to determine whether the provided codes {1} are in the value set {0} because the value set or a code system it depends on is not known to the validator
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = Unable to check whether the code is in the value set {0}
TERMINOLOGY_TX_SYSTEM_WRONG_HTML = The code system reference {0} is wrong - the code system reference cannot be to an HTML page. This may be the correct reference: {1}
TERMINOLOGY_TX_SYSTEM_WRONG_BUILD = The code system reference {0} is wrong - the code system reference cannot be a reference to build.fhir.org. This may be the correct reference: {1}
FHIRPATH_BAD_DATE = Unable to parse Date {0}
@ -920,6 +920,7 @@ NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the l
SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified
SD_NO_CONTEXT_INV_WHEN_NOT_EXTENSION = The type is {0} so an extension context invariants should not be specified
SD_CONTEXT_SHOULD_NOT_BE_ELEMENT = Review the extension type: extensions should not have a context of {0} unless it''s really intended that they can be used anywhere
ED_PATH_WRONG_TYPE_MATCH = The path must be ''{0}'' not ''{1}'' when the type list is not constrained
ATTEMPT_TO_CHANGE_SLICING = The element at {0} defines the slicing {1} but then an element in the slicing {2} tries to redefine the slicing to {3}
REPEAT_SLICING_IGNORED = The element at {0} defines the slicing but then an element in the slicing {2} repeats it, which is ignored

View File

@ -156,6 +156,7 @@ public class BaseValidator implements IValidationContextResourceLoader {
private ValidationLevel level = ValidationLevel.HINTS;
protected Coding jurisdiction;
protected boolean allowExamples;
protected boolean forPublication;
public BaseValidator(IWorkerContext context, XVerExtensionManager xverManager) {
super();
@ -1242,7 +1243,16 @@ public class BaseValidator implements IValidationContextResourceLoader {
}
protected boolean isExampleUrl(String url) {
return Utilities.containsInList(url, "example.org", "acme.com", "acme.org");
return Utilities.containsInList(url, "example.org", "acme.com", "acme.org");
}
public boolean isForPublication() {
return forPublication;
}
public BaseValidator setForPublication(boolean forPublication) {
this.forPublication = forPublication;
return this;
}
}

View File

@ -505,7 +505,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private ValidationOptions baseOptions = new ValidationOptions();
private Map<String, CanonicalResourceLookupResult> crLookups = new HashMap<>();
private boolean logProgress;
private boolean forPublication;
public InstanceValidator(IWorkerContext theContext, IEvaluationContext hostServices, XVerExtensionManager xverManager) {
super(theContext, xverManager);
@ -2394,18 +2393,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
if (type.equals("dateTime")) {
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path,
e.primitiveValue()
.matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, e.primitiveValue()) && ok;
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ) && ok;
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok;
try {
DateTimeType dt = new DateTimeType(e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, ex.getMessage());
ok = false;
boolean dok = ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path,
e.primitiveValue()
.matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, "'"+e.primitiveValue()+"' doesn't meet format requirements for dateTime") && ok;
dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !hasTime(e.primitiveValue()) || hasTimeZone(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_TZ) && dok;
dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && dok;
if (dok) {
try {
DateTimeType dt = new DateTimeType(e.primitiveValue());
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_VALID, ex.getMessage());
dok = false;
}
}
ok = ok && dok;
}
if (type.equals("time")) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path,
@ -2419,15 +2421,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
if (type.equals("date")) {
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID) && ok;
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok;
try {
DateType dt = new DateType(e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID, ex.getMessage());
ok = false;
boolean dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1]))?)?"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID, "'"+e.primitiveValue()+"' doesn't meet format requirements for date");
dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && dok;
if (dok) {
try {
DateType dt = new DateType(e.primitiveValue());
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATE_VALID, ex.getMessage());
dok = false;
}
}
ok = ok && dok;
}
if (type.equals("base64Binary")) {
String encoded = e.primitiveValue();
@ -2511,15 +2516,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
if (type.equals("instant")) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path,
e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX, e.primitiveValue()) && ok;
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
try {
InstantType dt = new InstantType(e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID, ex.getMessage());
ok = false;
boolean dok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path,
e.primitiveValue().matches("-?[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REGEX, "'"+e.primitiveValue()+"' doesn't meet format requirements for instant)");
if (dok) {
try {
InstantType dt = new InstantType(e.primitiveValue());
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, yearIsValid(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DATETIME_REASONABLE, e.primitiveValue());
} catch (Exception ex) {
rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_INSTANT_VALID, ex.getMessage());
dok = false;
}
}
ok = ok && dok;
}
if (type.equals("code") && e.primitiveValue() != null) {
@ -5040,7 +5048,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (element.getType().equals("SearchParameter")) {
return new SearchParameterValidator(context, timeTracker, fpe, xverManager, jurisdiction).validateSearchParameter(errors, element, stack);
} else if (element.getType().equals("StructureDefinition")) {
return new StructureDefinitionValidator(context, timeTracker, fpe, wantCheckSnapshotUnchanged, xverManager, jurisdiction).validateStructureDefinition(errors, element, stack);
return new StructureDefinitionValidator(context, timeTracker, fpe, wantCheckSnapshotUnchanged, xverManager, jurisdiction, forPublication).validateStructureDefinition(errors, element, stack);
} else if (element.getType().equals("StructureMap")) {
return new StructureMapValidator(context, timeTracker, fpe, xverManager,profileUtilities, jurisdiction).validateStructureMap(errors, element, stack);
} else if (element.getType().equals("ValueSet")) {
@ -6527,14 +6535,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.logProgress = logProgress;
}
public boolean isForPublication() {
return forPublication;
}
public void setForPublication(boolean forPublication) {
this.forPublication = forPublication;
}
public boolean isDisplayWarnings() {
return baseOptions.isDisplayWarningMode();
}
@ -6543,4 +6543,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
baseOptions.setDisplayWarningMode(displayWarnings);
}
public InstanceValidator setForPublication(boolean forPublication) {
this.forPublication = forPublication;
return this;
}
}

View File

@ -60,13 +60,14 @@ public class StructureDefinitionValidator extends BaseValidator {
private FHIRPathEngine fpe;
private boolean wantCheckSnapshotUnchanged;
public StructureDefinitionValidator(IWorkerContext context, TimeTracker timeTracker, FHIRPathEngine fpe, boolean wantCheckSnapshotUnchanged, XVerExtensionManager xverManager, Coding jurisdiction) {
public StructureDefinitionValidator(IWorkerContext context, TimeTracker timeTracker, FHIRPathEngine fpe, boolean wantCheckSnapshotUnchanged, XVerExtensionManager xverManager, Coding jurisdiction, boolean forPublication) {
super(context, xverManager);
source = Source.InstanceValidator;
this.fpe = fpe;
this.timeTracker = timeTracker;
this.wantCheckSnapshotUnchanged = wantCheckSnapshotUnchanged;
this.jurisdiction = jurisdiction;
this.forPublication = forPublication;
}
public boolean validateStructureDefinition(List<ValidationMessage> errors, Element src, NodeStack stack) {
@ -89,6 +90,7 @@ public class StructureDefinitionValidator extends BaseValidator {
rule(errors, "2022-11-02", IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasType() && sd.getType().equals(base.getType()), I18nConstants.SD_CONSTRAINED_TYPE_NO_MATCH, sd.getType(), base.getType());
List<ValidationMessage> msgs = new ArrayList<>();
ProfileUtilities pu = new ProfileUtilities(context, msgs, null);
pu.setForPublication(forPublication);
pu.setXver(xverManager);
pu.setNewSlicingProcessing(!sd.hasFhirVersion() || VersionUtilities.isR4Plus(sd.getFhirVersion().toCode()));
pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir/R4/", sd.getName());
@ -121,7 +123,7 @@ public class StructureDefinitionValidator extends BaseValidator {
I18nConstants.SD_DERIVATION_KIND_MISMATCH, base.getKindElement().primitiveValue(), src.getChildValue("kind")) && ok;
}
}
} catch (FHIRException | IOException e) {
} catch (Exception e) {
rule(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage());
ok = false;
}

View File

@ -175,19 +175,19 @@ public class SnapShotGenerationXTests {
}
public void load(String version) throws FHIRFormatError, FileNotFoundException, IOException {
if (UtilitiesXTests.findTestResource("rX", "snapshot-generation", id + "-input.json"))
source = (StructureDefinition) XVersionLoader.loadJson(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", id + "-input.json"));
if (TestingUtilities.findTestResource("rX", "snapshot-generation", id + "-input.json"))
source = (StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.json"));
else
source = (StructureDefinition) XVersionLoader.loadXml(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", id + "-input.xml"));
source = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.xml"));
if (!fail)
expected = (StructureDefinition) XVersionLoader.loadXml(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", id + "-expected.xml"));
expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-expected.xml"));
if (!Utilities.noString(include))
included = (StructureDefinition) XVersionLoader.loadXml(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", include + ".xml"));
included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml"));
if (!Utilities.noString(register)) {
if (UtilitiesXTests.findTestResource("rX", "snapshot-generation", register + ".xml")) {
included = (StructureDefinition) XVersionLoader.loadXml(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", register + ".xml"));
if (TestingUtilities.findTestResource("rX", "snapshot-generation", register + ".xml")) {
included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", register + ".xml"));
} else {
included = (StructureDefinition) XVersionLoader.loadJson(version, UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", register + ".json"));
included = (StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", register + ".json"));
}
}
}
@ -404,7 +404,7 @@ public class SnapShotGenerationXTests {
public static Iterable<Object[]> data() throws ParserConfigurationException, IOException, FHIRFormatError, SAXException {
SnapShotGenerationTestsContext context = new SnapShotGenerationTestsContext();
Document tests = XMLUtil.parseToDom(UtilitiesXTests.loadTestResource("rX", "snapshot-generation", "manifest.xml"));
Document tests = XMLUtil.parseToDom(TestingUtilities.loadTestResource("rX", "snapshot-generation", "manifest.xml"));
Element test = XMLUtil.getFirstChild(tests.getDocumentElement());
List<Object[]> objects = new ArrayList<Object[]>();
while (test != null && test.getNodeName().equals("test")) {
@ -471,7 +471,7 @@ public class SnapShotGenerationXTests {
pu.sortDifferential(base, test.getOutput(), test.getOutput().getUrl(), errors, false);
if (!errors.isEmpty())
throw new FHIRException(errors.get(0));
IOUtils.copy(UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected.xml")));
IOUtils.copy(TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected.xml")));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-actual.xml")), test.getOutput());
Assertions.assertTrue(test.expected.equalsDeep(test.output), "Output does not match expected");
}
@ -545,7 +545,7 @@ public class SnapShotGenerationXTests {
File dst = new File(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected.xml"));
if (dst.exists())
dst.delete();
IOUtils.copy(UtilitiesXTests.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(dst));
IOUtils.copy(TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", test.getId() + "-expected.xml"), new FileOutputStream(dst));
new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-actual.xml")), output);
StructureDefinition t1 = test.expected.copy();
t1.setText(null);

View File

@ -280,6 +280,11 @@ public class TerminologyServiceTests {
if (vm.getCodeableConcept() != null) {
res.addParameter("codeableConcept", vm.getCodeableConcept());
}
if (vm.getUnknownSystems() != null) {
for (String s : vm.getUnknownSystems()) {
res.addParameter("x-caused-by-unknown-system", new UriType(s));
}
}
if (vm.getIssues().size() > 0) {
OperationOutcome oo = new OperationOutcome();
oo.getIssue().addAll(vm.getIssues());

View File

@ -202,6 +202,9 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
byte[] testCaseContent = TestingUtilities.loadTestResource("validator", JsonUtilities.str(content, "file")).getBytes(StandardCharsets.UTF_8);
// load and process content
FhirFormat fmt = determineFormat(content, testCaseContent);
if (fmt == null) {
throw new FHIRException("Unknown format in source "+JsonUtilities.str(content, "file"));
}
InstanceValidator val = vCurr.getValidator(fmt);
val.setWantCheckSnapshotUnchanged(true);
@ -238,6 +241,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
val.setValidationLanguage(content.get("language").getAsString());
else
val.setValidationLanguage(null);
val.setForPublication(content.has("for-publication") && "true".equals(content.get("for-publication").getAsString()));
if (content.has("default-version")) {
val.setBaseOptions(val.getBaseOptions().withVersionFlexible(content.get("default-version").getAsBoolean()));
} else {

View File

@ -1116,3 +1116,39 @@ v: {
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "77176002",
"display" : "Smoker"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-bloodGroup", "version": "0.5.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" : "Smoker",
"code" : "77176002",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "38341003",
"display" : "Hypertensive disorder, systemic arterial (disorder)"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-bloodGroup", "version": "0.5.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" : "High blood pressure",
"code" : "38341003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------

View File

@ -63,3 +63,35 @@ v: {
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://varnomen.hgvs.org",
"code" : "NC_000019.8:g.1171707G>A"
}, "url": "http://hl7.org/fhir/us/mcode/ValueSet/mcode-hgvs-vs", "version": "1.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: {
"severity" : "error",
"error" : "Error from server: Error parsing HGVS response: Error parsing JSON source: Unexpected char \"<\" in json stream at Line 0 (path=[])",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://varnomen.hgvs.org",
"code" : "NC_000019.8:g.1171707G>AXXX"
}, "url": "http://hl7.org/fhir/us/mcode/ValueSet/mcode-hgvs-vs", "version": "1.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: {
"severity" : "error",
"error" : "Error from server: Error parsing HGVS response: Error parsing JSON source: Unexpected char \"<\" in json stream at Line 0 (path=[])",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------

View File

@ -3167,3 +3167,279 @@ v: {
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "59408-5",
"display" : "O2 % BldC Oximetry"
},
{
"system" : "http://loinc.org",
"code" : "2708-6",
"display" : "Oxygen saturation in Arterial blood"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "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: {
"severity" : "error",
"error" : "Wrong Display Name 'O2 % BldC Oximetry' for http://loinc.org#59408-5 - should be one of 26 choices: 'Oxygen saturation in Arterial blood by Pulse oximetry, \"SaO2 % BldA PulseOx\", \"O2 SaO2\" (pl-PL), \"saturacja krwi tlenem\" (pl-PL), \"MFr O2\" (zh-CN), \"tO2\" (zh-CN), \"总氧\" (zh-CN), \"氧气 SaO2 动脉血 动脉血O2饱和度 可用数量表示的\" (zh-CN), \"定量性\" (zh-CN), \"数值型\" (zh-CN), \"数量型\" (zh-CN), \"连续数值型标尺 时刻\" (zh-CN), \"随机\" (zh-CN), \"随意\" (zh-CN), \"瞬间 肺部测量指标与呼吸机管理 脉搏血氧测定法\" (zh-CN), \"脉搏血氧定量\" (zh-CN), \"脉搏血氧测定\" (zh-CN), \"脉搏血氧仪 血氧测定法 饱和 饱和状态 饱和程度\" (zh-CN), \"O2-Sättigung\" (de-DE), \"Frazione di massa Gestione ventilazione polmonare Punto nel tempo (episodio) Sangue arterioso\" (it-IT), \"Oksijen doymuşluğu\" (tr-TR), \"Количественный Кровь артериальная Массовая доля Насыщение кислородом Оксигемометрия\" (ru-RU), \"Гемоксиметрия Точка во времени\" (ru-RU), \"Момент\" (ru-RU), \"zuurstofsaturatiemeting\" (nl-NL), \"O2 SatO2\" (fr-BE)' for the language(s) '--' (from Tx-Server)",
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "3150-0",
"display" : "Inhaled Oxygen Concentration"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "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" : "Inhaled oxygen concentration",
"code" : "3150-0",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"coding" : [{
"system" : "http://loinc.org",
"code" : "3151-8",
"display" : "Flow Rate"
}]
}, "url": "http://hl7.org/fhir/us/core/ValueSet/us-core-vital-signs", "version": "4.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "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: {
"severity" : "error",
"error" : "Wrong Display Name 'Flow Rate' for http://loinc.org#3151-8 - should be one of 37 choices: 'Inhaled oxygen flow rate, \"Inhaled O2 flow rate\", \"O2\" (zh-CN), \"tO2\" (zh-CN), \"总氧\" (zh-CN), \"氧气 体积速率(单位时间)\" (zh-CN), \"单位时间内体积的变化速率\" (zh-CN), \"流量 可用数量表示的\" (zh-CN), \"定量性\" (zh-CN), \"数值型\" (zh-CN), \"数量型\" (zh-CN), \"连续数值型标尺 吸入气\" (zh-CN), \"吸入气体\" (zh-CN), \"吸入的空气 所吸入的氧\" (zh-CN), \"已吸入的氧气 时刻\" (zh-CN), \"随机\" (zh-CN), \"随意\" (zh-CN), \"瞬间 气 气体类 空气\" (zh-CN), \"Inhaled O2\" (pt-BR), \"vRate\" (pt-BR), \"Volume rate\" (pt-BR), \"Flow\" (pt-BR), \"Point in time\" (pt-BR), \"Random\" (pt-BR), \"IhG\" (pt-BR), \"Inhaled Gas\" (pt-BR), \"Inspired\" (pt-BR), \"Quantitative\" (pt-BR), \"QNT\" (pt-BR), \"Quant\" (pt-BR), \"Quan\" (pt-BR), \"Gases\" (pt-BR), \"Clinico Gas inalati Punto nel tempo (episodio) Tasso di Volume\" (it-IT), \"Количественный Объемная скорость Точка во времени\" (ru-RU), \"Момент\" (ru-RU), \"ingeademde O2\" (nl-NL), \"O2-Zufuhr\" (de-AT)' for the language(s) '--' (from Tx-Server)",
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "883-9",
"display" : "ABO group [Type] in Blood"
}, "url": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult", "version": "4.0.1", "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" : "ABO group [Type] in Blood",
"code" : "883-9",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "883-9"
}, "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" : "ABO group [Type] in Blood",
"code" : "883-9",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "46418-0",
"display" : "INR in Capillary blood by Coagulation assay"
}, "url": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult", "version": "4.0.1", "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" : "INR in Capillary blood by Coagulation assay",
"code" : "46418-0",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "77140-2",
"display" : "Creatinine [Moles/volume] in Serum, Plasma or Blood"
}, "url": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult", "version": "4.0.1", "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" : "Creatinine [Moles/volume] in Serum, Plasma or Blood",
"code" : "77140-2",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "4535-1",
"display" : "Cytotoxic percent reactive Ab [Presence] in Serum by Quick method"
}, "url": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult", "version": "4.0.1", "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" : "Cytotoxic percent reactive Ab [Presence] in Serum by Quick method",
"code" : "4535-1",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "29463-7",
"display" : "Body weight"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-laboratory", "version": "0.5.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" : "Body weight",
"code" : "29463-7",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "8302-2",
"display" : "Body height"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-laboratory", "version": "0.5.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" : "Body height",
"code" : "8302-2",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "39156-5",
"display" : "Body mass index (BMI) [Ratio]"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-laboratory", "version": "0.5.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" : "Body mass index (BMI) [Ratio]",
"code" : "39156-5",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "85354-9",
"display" : "Blood pressure panel with all children optional"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-laboratory", "version": "0.5.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" : "Blood pressure panel with all children optional",
"code" : "85354-9",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "883-9",
"display" : "ABO group [Type] in Blood"
}, "url": "https://mednet.swiss/fhir/ValueSet/mni-obs-laboratory", "version": "0.5.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" : "ABO group [Type] in Blood",
"code" : "883-9",
"system" : "http://loinc.org",
"version" : "2.74"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "X-34133-9"
}, "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: {
"severity" : "error",
"error" : "Unknown Code 'X-34133-9' in the system 'http://loinc.org'; The provided code http://loinc.org#X-34133-9 is not in the value set 'http://hl7.org/fhir/ValueSet/@all' (from Tx-Server)",
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://loinc.org",
"code" : "5792-7"
}, "valueSet" :{
"resourceType" : "ValueSet"
}, "langs":"[]", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"CHECK_MEMERSHIP_ONLY", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"severity" : "error",
"error" : "Error from server: Unable to resolve value set \"http://hl7.org/fhir/ValueSet/birthDate\"",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------

View File

@ -2116,3 +2116,37 @@ v: {
"class" : "UNKNOWN"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "77176002"
}, "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" : "Smoker",
"code" : "77176002",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "38341003"
}, "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" : "High blood pressure",
"code" : "38341003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,19 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://dicom.nema.org/resources/ontology/DCM",
"code" : "110122",
"display" : "Login"
}, "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" : "Login",
"code" : "110122",
"system" : "http://dicom.nema.org/resources/ontology/DCM",
"version" : "2023.1.20230123"
}
-------------------------------------------------------------------------------------

View File

@ -752,3 +752,73 @@ v: {
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "90655003",
"display" : "Geriatrics specialist"
}, "url": "http://hl7.org/fhir/ValueSet/c80-practice-codes", "version": "5.0.0", "langs":"[en]", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "displayWarningMode":"false", "versionFlexible":"true", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"display" : "Geriatrics specialist",
"code" : "90655003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "90655003"
}, "valueSet" :null, "langs":"[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" : "Geriatrics specialist",
"code" : "90655003",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "106004",
"display" : "Posterior carpal region"
}, "url": "http://hl7.org/fhir/ValueSet/clinical-findings", "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" : "Posterior carpal region",
"code" : "106004",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://snomed.info/sct",
"code" : "106004"
}, "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" : "Posterior carpal region",
"code" : "106004",
"system" : "http://snomed.info/sct",
"version" : "http://snomed.info/sct/900000000000207008/version/20230131"
}
-------------------------------------------------------------------------------------

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.3.6</validator_test_case_version>
<validator_test_case_version>1.3.7-SNAPSHOT</validator_test_case_version>
<jackson_version>2.14.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>