/// <summary> /// Generate snapshots for all StructureDefinitions available to the preprocessor /// </summary> /// <returns></returns> public static OperationOutcome GenerateSnapshots(IEnumerable <StructureDefinition> sds, Action <StructureDefinition> snapshotGenerator, string path) { var outcome = new OperationOutcome(); foreach (var sd in sds) { if (!sd.HasSnapshot) { try { snapshotGenerator(sd); } catch (Exception e) { outcome.AddIssue($"Snapshot generation failed for '{sd.Url}'. Message: {e.Message}", Issue.UNAVAILABLE_SNAPSHOT_GENERATION_FAILED, path); } } if (!sd.HasSnapshot) { outcome.AddIssue($"Profile '{sd.Url}' does not include a snapshot.", Issue.UNAVAILABLE_NEED_SNAPSHOT, path); } } return(outcome); }
public void Setup() { OperationOutcome level0 = new OperationOutcome(); level0.AddIssue(Issue.CONTENT_ELEMENT_HAS_INCORRECT_TYPE.ToIssueComponent("A test error at level 0", "Patient")); OperationOutcome level1 = new OperationOutcome(); Patient p = new Patient(); p.Active = true; _location = new PocoNavigator(p); _location.MoveToFirstChild(); level1.AddIssue(Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING.ToIssueComponent("A test warning at level 1", _location)); OperationOutcome level2 = new OperationOutcome(); level2.AddIssue(Issue.UNSUPPORTED_CONSTRAINT_WITHOUT_FHIRPATH.ToIssueComponent("A test warning at level 2", "Patient.active[0].id[0]")); level2.AddIssue(Issue.CONTENT_ELEMENT_MUST_MATCH_TYPE.ToIssueComponent("Another test error at level 2", "Patient.active[0].id[0]")); level1.Include(level2); level0.Include(level1); level0.AddIssue(Issue.PROFILE_ELEMENTDEF_IS_EMPTY.ToIssueComponent("A test warning at level 0", "Patient")); _report = level0; }
private OperationOutcome processResult(string code, string system, string display, Coding coding, CodeableConcept codeableConcept, ValidateCodeResult result) { if (result?.Result?.Value == null) { throw Error.InvalidOperation($"Terminology service at {Endpoint.Endpoint.ToString()} did not return a result."); } var outcome = new OperationOutcome(); if (result?.Result?.Value == false) { string message = result?.Message?.Value; if (message != null) { outcome.AddIssue(message, Issue.TERMINOLOGY_CODE_NOT_IN_VALUESET); } else { if (code != null && coding == null) { coding = new Coding(system, code, display); } var codeDisplay = codeableConcept != null?FhirSerializer.SerializeToJson(codeableConcept) : FhirSerializer.SerializeToJson(coding); outcome.AddIssue($"Validation of '{codeDisplay}' failed, but" + $"the terminology service at {Endpoint.Endpoint.ToString()} did not provide further details.", Issue.TERMINOLOGY_CODE_NOT_IN_VALUESET); } } return(outcome); }
public OperationOutcome ValidateBinding(IElementNavigator instance, ElementDefinition definition) { var outcome = new OperationOutcome(); if (definition.Binding != null) { var binding = definition.Binding; if (binding.ValueSet != null) { var uri = (binding.ValueSet as FhirUri)?.Value; // == null, so we check whether this could NOT be casted to a FhirUri, thus is a ValueSet reference if (uri == null) { uri = (binding.ValueSet as ResourceReference).Reference; var codedType = Validator.DetermineType(definition, instance); if (codedType != null) { if (codedType.Value.IsBindeableFhirType()) { var bindable = instance.ParseBindable(codedType.Value); if (bindable is Coding) { return(ValidateBinding(bindable as Coding, uri, binding.Strength)); } else { return(ValidateBinding(bindable as CodeableConcept, uri, binding.Strength)); } } else { outcome.AddIssue($"A binding is given ('{uri}'), but the instance data is of type '{codedType.Value}', which is not bindeable.", Issue.CONTENT_TYPE_NOT_BINDEABLE, instance); } } else { outcome.AddIssue($"Cannot determine type of data in instance to extract code/system information", Issue.CONTENT_ELEMENT_CANNOT_DETERMINE_TYPE, instance); } } else { outcome.AddIssue($"Binding element references a valueset by uri ({uri}), which cannot be used to validate a binding", Issue.UNSUPPORTED_URI_BINDING_NOT_SUPPORTED, instance); } } else { outcome.AddIssue($"Encountered a binding element without a ValueSet reference", Issue.PROFILE_BINDING_WITHOUT_VALUESET, instance); } } return(outcome); }
public OperationOutcome ValidateCode(string uri, string code, string system, string display = null, bool abstractAllowed = false) { var result = new OperationOutcome(); var vs = _resolver.FindValueSet(uri); if (vs == null) { result.AddIssue($"Cannot retrieve valueset '{uri}'", Issue.UNAVAILABLE_VALUESET); return(result); } // We might have a cached or pre-expanded version brought to us by the _source if (!vs.HasExpansion) { // This will expand te vs - since we do not deepcopy() it, it will change the instance // as it was passed to us from the source try { _expander.Expand(vs); } catch (Exception e) { var tooComplex = e is NotSupportedException || e is ValueSetExpansionTooBigException; result.AddIssue($"ValueSet cannot be expanded: {e.Message}", tooComplex ? Issue.TERMINOLOGY_VALUESET_TOO_COMPLEX : Issue.TERMINOLOGY_EXPANSION_FAILED); return(result); } } // No fallback necessary, just do a direct check for now var component = vs.FindInExpansion(code, system); var codeLabel = $"Code '{code}' from system '{system}'"; if (component == null) { result.AddIssue($"{codeLabel} does not exists in valueset '{uri}'", Issue.TERMINOLOGY_CODE_NOT_IN_VALUESET); } else { if (component.Abstract == true && !abstractAllowed) { result.AddIssue($"{codeLabel} is abstract, which is not allowed here", Issue.TERMINOLOGY_ABSTRACT_CODE_NOT_ALLOWED); } if (display != null && component.Display != null && display != component.Display) { result.AddIssue($"{codeLabel} has incorrect display '{display}', should be '{component.Display}'", Issue.TERMINOLOGY_INCORRECT_DISPLAY); } } return(result); }
// This is the one and only main internal entry point for all validations, which in its term // will call step 1 in the validator, the function validateElement internal OperationOutcome Validate(ScopedNavigator instance, IEnumerable <ElementDefinitionNavigator> definitions) { var outcome = new OperationOutcome(); try { List <ElementDefinitionNavigator> allDefinitions = new List <ElementDefinitionNavigator>(definitions); if (allDefinitions.Count() == 1) { outcome.Add(validateElement(allDefinitions.Single(), instance)); } else { var validators = allDefinitions.Select(nav => createValidator(nav, instance)); outcome.Add(this.Combine(BatchValidationMode.All, instance, validators)); } } catch (Exception e) { outcome.AddIssue($"Internal logic failure: {e.Message}", Issue.PROCESSING_CATASTROPHIC_FAILURE, instance); } return(outcome); }
internal void Trace(OperationOutcome outcome, string message, Issue issue, IElementNavigator location) { if (Settings.Trace || issue.Severity != OperationOutcome.IssueSeverity.Information) { outcome.AddIssue(message, issue, location); } }
public static OperationOutcome.IssueComponent AddIssue(this OperationOutcome outcome, string message, Issue infoIssue, string location = null) { var issue = infoIssue.ToIssueComponent(message, location); outcome.AddIssue(issue); return(issue); }
public static OperationOutcome NewOutcomeWithIssue(this Issue infoIssue, string message, string location = null) { var outcome = new OperationOutcome(); var issue = infoIssue.ToIssueComponent(message, location); outcome.AddIssue(issue); return(outcome); }
internal OperationOutcome.IssueComponent Trace(OperationOutcome outcome, string message, Issue issue, string location) { if (Settings.Trace || issue.Severity != OperationOutcome.IssueSeverity.Information) { return(outcome.AddIssue(message, issue, location)); } return(null); }
public static void Add(this OperationOutcome outcome, OperationOutcome other) { outcome.AddIssue(other.Issue); //foreach (var issue in other.Issue) //{ // //var myIssue = (OperationOutcome.IssueComponent)issue.DeepCopy(); // var myIssue = issue; // outcome.AddIssue(myIssue); //} }
internal static OperationOutcome ValidateXml(this Validator me, XDocument instance) { var result = new OperationOutcome(); ValidationEventHandler veh = (o, args) => result.AddIssue(ToIssueComponent(args)); instance.Validate(SchemaCollection.ValidationSchemaSet, veh); return(result); }
public static void Include(this OperationOutcome outcome, OperationOutcome other) { foreach (var issue in other.Issue) { // var myIssue = (OperationOutcome.IssueComponent)issue.DeepCopy(); var myIssue = issue; myIssue.SetHierarchyLevel(myIssue.GetHierarchyLevel() + 1); outcome.AddIssue(myIssue); } }
private static OperationOutcome validateMinMaxValue(Validator me, Element definition, ITypedElement instance, int comparisonOutcome, string elementName) { var outcome = new OperationOutcome(); if (definition != null) { // Min/max are only defined for ordered types if (definition.GetType().IsOrderedFhirType()) { try { var instanceValue = instance.GetComparableValue(definition.GetType()); if (instanceValue != null) { if (Compare(instanceValue, definition) == comparisonOutcome) { var label = comparisonOutcome == -1 ? "smaller than" : comparisonOutcome == 0 ? "equal to" : "larger than"; var issue = comparisonOutcome == -1 ? Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_SMALL : Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_LARGE; outcome.AddIssue($"Instance value '{instanceValue}' is {label} {elementName} '{definition}'", issue, instance); } } } catch (NotSupportedException ns) { outcome.AddIssue($"Comparing the instance against the {elementName} failed: {ns.Message}", Issue.UNSUPPORTED_MIN_MAX_QUANTITY, instance); } } else { me.Trace(outcome, $"{elementName} was given in ElementDefinition, but type '{definition.TypeName}' is not an ordered type", Issue.PROFILE_ELEMENTDEF_MIN_MAX_USES_UNORDERED_TYPE, instance); } } return(outcome); }
private OperationOutcome validateCodeVS(ValueSet vs, string code, string system, string display, bool?abstractAllowed) { if (code == null) { return(Issue.TERMINOLOGY_NO_CODE_IN_INSTANCE.NewOutcomeWithIssue($"No code supplied.")); } lock (vs.SyncLock) { // We might have a cached or pre-expanded version brought to us by the _source if (!vs.HasExpansion) { // This will expand te vs - since we do not deepcopy() it, it will change the instance // as it was passed to us from the source _expander.Expand(vs); } } var component = vs.FindInExpansion(code, system); var codeLabel = $"Code '{code}' from system '{system}'"; var result = new OperationOutcome(); if (component == null) { result.AddIssue($"{codeLabel} does not exist in valueset '{vs.Url}'", Issue.TERMINOLOGY_CODE_NOT_IN_VALUESET); } else { if (component.Abstract == true && abstractAllowed == false) // will be ignored if abstractAllowed == null { result.AddIssue($"{codeLabel} is abstract, which is not allowed here", Issue.TERMINOLOGY_ABSTRACT_CODE_NOT_ALLOWED); } if (display != null && component.Display != null && display != component.Display) { result.AddIssue($"{codeLabel} has incorrect display '{display}', should be '{component.Display}'", Issue.TERMINOLOGY_INCORRECT_DISPLAY); } } return(result); }
private OperationOutcome callService(string code, string system, string display, string uri, BindingStrength?strength, string path) { var outcome = new OperationOutcome(); OperationOutcome validateResult = _service.ValidateCode(uri, code, system, display, abstractAllowed: false); var codeLabel = $"Code '{code}' from system '{system}'"; if (display != null) { codeLabel += $" with display '{display}'"; } if (validateResult.Where(type: OperationOutcome.IssueType.NotSupported).Any()) { if (strength != BindingStrength.Example) { outcome.AddIssue($"The terminology service is incapable of validating {codeLabel} (valueset '{uri}').", Issue.UNSUPPORTED_BINDING_NOT_SUPPORTED_BY_SERVICE, path); validateResult.MakeInformational(); outcome.Include(validateResult); } return(outcome); } if (!validateResult.Success) { if (strength == BindingStrength.Required) { outcome.AddIssue($"{codeLabel} is not valid for required binding to valueset '{uri}'", Issue.CONTENT_INVALID_FOR_REQUIRED_BINDING, path); } else if (strength != BindingStrength.Example) { outcome.AddIssue($"{codeLabel} is not valid for non-required binding to valueset '{uri}'", Issue.CONTENT_INVALID_FOR_NON_REQUIRED_BINDING, path); } validateResult.MakeInformational(); } outcome.Include(validateResult); return(outcome); }
/// <summary> /// Resolves the StructureDefinitions referred to by the given canonicals, and adds them /// to the list of StructureDefinitions available to the preprocessor /// </summary> /// <returns></returns> public OperationOutcome Resolve() { if (_lastResolutionOutcome != null) { return(_lastResolutionOutcome); } var outcome = new OperationOutcome(); // Go through all entries to find those that don't yet have a StructureDefinition foreach (var entry in _allEntries.Where(e => e.Unresolved)) { StructureDefinition structureDefinition = null; try { // Once FindStructureDefinition() gets an overload to resolve multiple at the same time, // use that one structureDefinition = _resolver(entry.Reference); if (structureDefinition == null) { outcome.AddIssue($"Unable to resolve reference to profile '{entry.Reference}'", Issue.UNAVAILABLE_REFERENCED_PROFILE, _path); } else { entry.StructureDefinition = structureDefinition; } } catch (Exception e) { outcome.AddIssue($"Resolution of profile at '{entry.Reference}' failed: {e.Message}", Issue.UNAVAILABLE_REFERENCED_PROFILE, _path); continue; } } _lastResolutionOutcome = outcome; return(outcome); }
/// <summary> /// Generate snapshots for all StructureDefinitions available to the preprocessor /// </summary> /// <returns></returns> public static OperationOutcome GenerateSnapshots(IEnumerable <StructureDefinition> sds, Func <StructureDefinition, OperationOutcome> snapshotGenerator, string path) { var outcome = new OperationOutcome(); foreach (var sd in sds) { lock (sd.SyncLock) { if (!sd.HasSnapshot) { try { var snapshotOutcome = snapshotGenerator(sd); // [WMR 20180725] Null outcome also indicates success if (snapshotOutcome != null && !snapshotOutcome.Success) { outcome.AddIssue($"Snapshot generation failed for '{sd.Url}'. Details follow below.", Issue.UNAVAILABLE_SNAPSHOT_GENERATION_FAILED, path); outcome.Add(snapshotOutcome); } } catch (Exception e) { outcome.AddIssue($"Snapshot generation failed for '{sd.Url}'. Message: {e.Message}", Issue.UNAVAILABLE_SNAPSHOT_GENERATION_FAILED, path); } } } if (!sd.HasSnapshot) { outcome.AddIssue($"Profile '{sd.Url}' does not include a snapshot.", Issue.UNAVAILABLE_NEED_SNAPSHOT, path); } } return(outcome); }
OperationOutcome.IssueComponent addIssue(OperationOutcome.IssueComponent component, string profileUrl = null) { if (component == null) { throw Error.ArgumentNull(nameof(component)); } if (_outcome == null) { _outcome = new OperationOutcome(); } // Return current profile url in Diagnostics field component.Diagnostics = profileUrl ?? CurrentProfileUri; _outcome.AddIssue(component); return(component); }
private OperationOutcome callService(string canonical, string code = null, string system = null, string display = null, Coding coding = null, CodeableConcept cc = null, bool?abstractAllowed = null) { var outcome = new OperationOutcome(); try { outcome = _service.ValidateCode(canonical: canonical, code: code, system: system, display: display, coding: coding, codeableConcept: cc, @abstract: abstractAllowed); foreach (var issue in outcome.Issue) { issue.Location = new string[] { _path } } ; } catch (TerminologyServiceException tse) { outcome.AddIssue($"Terminology service failed while validating code '{code}' (system '{system}'): {tse.Message}", Issue.TERMINOLOGY_SERVICE_FAILED, _path); } return(outcome); }
public OperationOutcome Process() { var outcome = new OperationOutcome(); // Start preprocessing by resolving the references to the profiles (if any) var resolveOutcome = _profiles.Resolve(); outcome.Add(resolveOutcome); if (resolveOutcome.Success) { // Then, validate consistency of the profile assertions var validationOutcome = _profiles.Validate(); outcome.Add(validationOutcome); if (validationOutcome.Success) { if (_profiles.MinimalProfiles.Any()) { // Then, generate snapshots for all sds that we have found var genSnapshotOutcome = GenerateSnapshots(_profiles.MinimalProfiles, _snapshotGenerator, _path); outcome.Add(genSnapshotOutcome); if (genSnapshotOutcome.Success) { // Finally, return navigators to the definitions Result = CreateNavigators(_profiles.MinimalProfiles); } } else { outcome.AddIssue("There are no profile and type assertions at this point in the instance, so validation cannot succeed", Issue.PROFILE_NO_PROFILE_TO_VALIDATE_AGAINST, _path); } } } return(outcome); }
internal static OperationOutcome ValidatedParseXml(this Validator me, XmlReader instance, out Resource poco) { var result = new OperationOutcome(); try { if (me.Settings.EnableXsdValidation) { var doc = XDocument.Load(instance, LoadOptions.SetLineInfo); result.Add(me.ValidateXml(doc)); instance = doc.CreateReader(); } poco = (Resource)(new FhirXmlParser()).Parse(instance, typeof(Resource)); } catch (Exception e) { result.AddIssue($"Parsing of Xml into a FHIR Poco failed: {e.Message}", Issue.XSD_CONTENT_POCO_PARSING_FAILED, (string)null); poco = null; } return(result); }
private OperationOutcome validateCodeVS(ValueSet vs, CodeableConcept cc, bool?abstractAllowed) { var outcome = new OperationOutcome(); // Maybe just a text, but if there are no codings, that's a positive result if (!cc.Coding.Any()) { return(outcome); } // If we have just 1 coding, we better handle this using the simpler version of ValidateBinding if (cc.Coding.Count == 1) { return(validateCodeVS(vs, cc.Coding.Single(), abstractAllowed)); } // Else, look for one succesful match in any of the codes in the CodeableConcept var callResults = cc.Coding.Select(coding => validateCodeVS(vs, coding, abstractAllowed)); var successOutcome = callResults.Where(r => r.Success).OrderBy(oo => oo.Warnings).FirstOrDefault(); if (successOutcome == null) { outcome.AddIssue("None of the Codings in the CodeableConcept were valid for the binding. Details follow.", Issue.CONTENT_INVALID_FOR_REQUIRED_BINDING); foreach (var cr in callResults) { cr.MakeInformational(); outcome.Include(cr); } } else { outcome.Add(successOutcome); } return(outcome); }
public static void AddIssue(this OperationOutcome outcome, params OperationOutcome.IssueComponent[] issue) { outcome.AddIssue((IEnumerable <OperationOutcome.IssueComponent>)issue); }
public static OperationOutcome AddError(this OperationOutcome outcome, string message) { return(outcome.AddIssue(OperationOutcome.IssueSeverity.Error, message)); }
public static OperationOutcome AddMessage(this OperationOutcome outcome, HttpStatusCode code, string message) { return(outcome.AddIssue(IssueSeverityOf(code), message)); }
public static OperationOutcome AddMessage(this OperationOutcome outcome, string message) { return(outcome.AddIssue(OperationOutcome.IssueSeverity.Information, message)); }
public static OperationOutcome Combine(this Validator validator, BatchValidationMode mode, ITypedElement instance, IEnumerable <Func <OperationOutcome> > validations) { if (validations.Count() == 0) { return(new OperationOutcome()); } if (validations.Count() == 1) { // To not pollute the output if there's just a single input, just add it to the output return(validations.First()()); } OperationOutcome combinedResult = new OperationOutcome(); var modeLabel = mode == BatchValidationMode.All ? "ALL" : "ANY"; validator.Trace(combinedResult, $"Combination of {validations.Count()} child validation runs, {modeLabel} must succeed", Issue.PROCESSING_PROGRESS, instance); int failures = 0; int successes = 0; List <OperationOutcome> results = new List <OperationOutcome>(); // Run the given validations one by one, short circuiting when ANY success is enough foreach (var validation in validations) { var result = validation(); results.Add(result); if (result.Success) { successes += 1; } else { failures += 1; } if (mode == BatchValidationMode.Any && successes > 0) { break; // shortcut evaluation } } // Did we have success overall? bool success = mode == BatchValidationMode.Any && successes > 0 || mode == BatchValidationMode.All && failures == 0 || mode == BatchValidationMode.Once && successes == 1; // Now, build final report for (var index = 0; index < results.Count; index++) { var result = results[index]; validator.Trace(combinedResult, $"Report {index}: {(result.Success ? "SUCCESS" : "FAILURE")}", Issue.PROCESSING_PROGRESS, instance); if (success) { // We'd like to include all results of the combined reports, but if the total result is a success, // any errors in failing runs should just be informational if (!result.Success) { result.MakeInformational(); } } combinedResult.Include(result); } if (success) { validator.Trace(combinedResult, "Combined validation succeeded", Issue.PROCESSING_PROGRESS, instance); } else { combinedResult.AddIssue($"Combined {modeLabel} validation failed, {failures} child validation runs failed, {successes} succeeded", Issue.PROCESSING_PROGRESS, instance); } return(combinedResult); }
public static void AddIssue(this OperationOutcome outcome, string message, Issue infoIssue, string location = null) { outcome.AddIssue(infoIssue.ToIssueComponent(message, location)); }
public static void AddIssue(this OperationOutcome outcome, string message, Issue infoIssue, IElementNavigator location) { outcome.AddIssue(infoIssue.ToIssueComponent(message, location)); }