private static Dictionary <CompartmentType, (CompartmentType, Uri, IList <(string, IList <string>)>)> ValidateAndGetCompartmentDict(BundleWrapper bundle) { EnsureArg.IsNotNull(bundle, nameof(bundle)); var issues = new List <OperationOutcomeIssue>(); var validatedCompartments = new Dictionary <CompartmentType, (CompartmentType, Uri, IList <(string, IList <string>)>)>(); IReadOnlyList <BundleEntryWrapper> entries = bundle.Entries; for (int entryIndex = 0; entryIndex < entries.Count; entryIndex++) { // Make sure resources are not null and they are Compartment. BundleEntryWrapper entry = entries[entryIndex]; var compartment = entry.Resource; if (compartment == null || !string.Equals(KnownResourceTypes.CompartmentDefinition, compartment.InstanceType, StringComparison.Ordinal)) { AddIssue(Core.Resources.CompartmentDefinitionInvalidResource, entryIndex); continue; } string code = compartment.Scalar("code")?.ToString(); string url = compartment.Scalar("url")?.ToString(); if (code == null) { AddIssue(Core.Resources.CompartmentDefinitionInvalidCompartmentType, entryIndex); continue; } CompartmentType typeCode = EnumUtility.ParseLiteral <CompartmentType>(code).GetValueOrDefault(); if (validatedCompartments.ContainsKey(typeCode)) { AddIssue(Core.Resources.CompartmentDefinitionIsDupe, entryIndex); continue; } if (string.IsNullOrWhiteSpace(url) || !Uri.IsWellFormedUriString(url, UriKind.Absolute)) { AddIssue(Core.Resources.CompartmentDefinitionInvalidUrl, entryIndex); continue; } var resources = compartment.Select("resource") .Select(x => (x.Scalar("code")?.ToString(), (IList <string>)x.Select("param").AsStringValues().ToList())) .ToList(); var resourceNames = resources.Select(x => x.Item1).ToArray(); if (resourceNames.Length != resourceNames.Distinct().Count()) { AddIssue(Core.Resources.CompartmentDefinitionDupeResource, entryIndex); continue; } validatedCompartments.Add( typeCode, (typeCode, new Uri(url), new List <(string, IList <string>)>(resources))); } if (issues.Count != 0) { throw new InvalidDefinitionException( Core.Resources.CompartmentDefinitionContainsInvalidEntry, issues.ToArray()); } return(validatedCompartments); void AddIssue(string format, params object[] args) { issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Fatal, OperationOutcomeConstants.IssueType.Invalid, string.Format(CultureInfo.InvariantCulture, format, args))); } }
private List <(string ResourceType, SearchParameterInfo SearchParameter)> ValidateAndGetFlattenedList() { var issues = new List <OperationOutcomeIssue>(); BundleWrapper bundle = null; using (Stream stream = _modelInfoProvider.OpenVersionedFileStream(_embeddedResourceName, _embeddedResourceNamespace, _assembly)) { using TextReader reader = new StreamReader(stream); using JsonReader jsonReader = new JsonTextReader(reader); try { bundle = new BundleWrapper(FhirJsonNode.Read(jsonReader).ToTypedElement(_modelInfoProvider.StructureDefinitionSummaryProvider)); } catch (FormatException ex) { AddIssue(ex.Message); } } EnsureNoIssues(); IReadOnlyList <BundleEntryWrapper> entries = bundle.Entries; // Do the first pass to make sure all resources are SearchParameter. for (int entryIndex = 0; entryIndex < entries.Count; entryIndex++) { // Make sure resources are not null and they are SearchParameter. BundleEntryWrapper entry = entries[entryIndex]; ITypedElement searchParameterElement = entry.Resource; if (searchParameterElement == null || !string.Equals(searchParameterElement.InstanceType, KnownResourceTypes.SearchParameter, StringComparison.OrdinalIgnoreCase)) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidResource, entryIndex); continue; } var searchParameter = new SearchParameterWrapper(searchParameterElement); try { SearchParameterInfo searchParameterInfo = CreateSearchParameterInfo(searchParameter); _uriDictionary.Add(new Uri(searchParameter.Url), searchParameterInfo); } catch (FormatException) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidDefinitionUri, entryIndex); continue; } catch (ArgumentException) { AddIssue(Core.Resources.SearchParameterDefinitionDuplicatedEntry, searchParameter.Url); continue; } } EnsureNoIssues(); var validatedSearchParameters = new List <(string ResourceType, SearchParameterInfo SearchParameter)> { // _type is currently missing from the search params definition bundle, so we inject it in here. (KnownResourceTypes.Resource, new SearchParameterInfo(SearchParameterNames.ResourceType, SearchParamType.Token, SearchParameterNames.ResourceTypeUri, null, "Resource.type().name", null)), }; // Do the second pass to make sure the definition is valid. for (int entryIndex = 0; entryIndex < entries.Count; entryIndex++) { BundleEntryWrapper entry = entries[entryIndex]; ITypedElement searchParameterElement = entry.Resource; var searchParameter = new SearchParameterWrapper(searchParameterElement); // If this is a composite search parameter, then make sure components are defined. if (string.Equals(searchParameter.Type, SearchParamType.Composite.GetLiteral(), StringComparison.OrdinalIgnoreCase)) { var composites = searchParameter.Component; if (composites.Count == 0) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidComponent, searchParameter.Url); continue; } for (int componentIndex = 0; componentIndex < composites.Count; componentIndex++) { ITypedElement component = composites[componentIndex]; var definitionUrl = GetComponentDefinition(component); if (definitionUrl == null || !_uriDictionary.TryGetValue(new Uri(definitionUrl), out SearchParameterInfo componentSearchParameter)) { AddIssue( Core.Resources.SearchParameterDefinitionInvalidComponentReference, searchParameter.Url, componentIndex); continue; } if (componentSearchParameter.Type == SearchParamType.Composite) { AddIssue( Core.Resources.SearchParameterDefinitionComponentReferenceCannotBeComposite, searchParameter.Url, componentIndex); continue; } if (string.IsNullOrWhiteSpace(component.Scalar("expression")?.ToString())) { AddIssue( Core.Resources.SearchParameterDefinitionInvalidComponentExpression, searchParameter.Url, componentIndex); continue; } } } // Make sure the base is defined. var bases = searchParameter.Base; if (bases.Count == 0) { AddIssue(Core.Resources.SearchParameterDefinitionBaseNotDefined, searchParameter.Url); continue; } for (int baseElementIndex = 0; baseElementIndex < bases.Count; baseElementIndex++) { var code = bases[baseElementIndex]; string baseResourceType = code; // Make sure the expression is not empty unless they are known to have empty expression. // These are special search parameters that searches across all properties and needs to be handled specially. if (ShouldExcludeEntry(baseResourceType, searchParameter.Name) || (_modelInfoProvider.Version == FhirSpecification.R5 && _knownBrokenR5.Contains(new Uri(searchParameter.Url)))) { continue; } else { if (string.IsNullOrWhiteSpace(searchParameter.Expression)) { AddIssue(Core.Resources.SearchParameterDefinitionInvalidExpression, searchParameter.Url); continue; } } validatedSearchParameters.Add((baseResourceType, CreateSearchParameterInfo(searchParameter))); } } EnsureNoIssues(); return(validatedSearchParameters); void AddIssue(string format, params object[] args) { issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Fatal, OperationOutcomeConstants.IssueType.Invalid, string.Format(CultureInfo.InvariantCulture, format, args))); } void EnsureNoIssues() { if (issues.Count != 0) { throw new InvalidDefinitionException( Core.Resources.SearchParameterDefinitionContainsInvalidEntry, issues.ToArray()); } } }