public static ListedCapabilityStatement TryAddRestInteraction(this ListedCapabilityStatement statement, ResourceType resourceType, CapabilityStatement.TypeRestfulInteraction interaction) { EnsureArg.IsNotNull(statement, nameof(statement)); statement.BuildRestResourceComponent(resourceType, builder => { var hasInteraction = builder .Interaction .FirstOrDefault(x => x.Code == interaction); if (hasInteraction == null) { builder .Interaction .Add(new CapabilityStatement.ResourceInteractionComponent { Code = interaction, }); } }); return(statement); }
public static CapabilityStatement Intersect(this ListedCapabilityStatement system, CapabilityStatement configured, bool strictConfig) { EnsureArg.IsNotNull(system, nameof(system)); EnsureArg.IsNotNull(configured, nameof(configured)); var issues = new List <string>(); var intersecting = new CapabilityStatement { // System wide values Id = system.Id, Url = system.Url?.OriginalString, Version = system.Version, Name = system.Name, Experimental = system.Experimental, Publisher = system.Publisher, Software = system.Software, FhirVersion = system.FhirVersion, Contact = new List <ContactDetail> { new ContactDetail { Telecom = system.Telecom?.Select(x => new ContactPoint(x.System, x.Use, x.Value)).ToList() } }, // Intersections with user configured values Kind = system.Kind.IntersectEnum(configured.Kind, issues, "Kind"), Status = system.Status.IntersectEnum(configured.Status, issues, "Status"), AcceptUnknown = system.AcceptUnknown.IntersectEnum(configured.AcceptUnknown, issues, "AcceptUknown"), Format = system.Format?.IntersectList(configured.Format, x => x, issues, "Format"), }; DateTimeOffset cDate; if (DateTimeOffset.TryParse(configured.Date, out cDate)) { intersecting.Date = cDate.ToString("o", CultureInfo.InvariantCulture); } if (system.Rest.Any() && configured.Rest.Any()) { // Only a single rest node is currently supported if (system.Rest.Count() > 1 || configured.Rest.Count > 1) { throw new NotSupportedException(Core.Resources.CapabilityStatementSingleRestItem); } var systemRest = system.Rest.Single(); var configuredRest = configured.Rest.Single(); var rest = new CapabilityStatement.RestComponent { Mode = systemRest.Mode.IntersectEnum(configuredRest.Mode, issues, "Rest.Mode"), Documentation = systemRest.Documentation, Security = systemRest.Security, Interaction = systemRest.Interaction?.IntersectList(configuredRest.Interaction, x => x.Code, issues, $"Rest.Interaction"), SearchParam = systemRest.SearchParam?.IntersectList(configuredRest.SearchParam, x => x.Name, issues, $"Rest.SearchParam"), Operation = systemRest.Operation?.IntersectList(configuredRest.Operation, x => x.Name, issues, $"Rest.Operation"), }; intersecting.Rest.Add(rest); var systemComponents = systemRest.Resource.Where(x => configuredRest.Resource.Select(r => r.Type).Contains(x.Type)); foreach (var systemComponent in systemComponents) { var configuredComponent = configuredRest.Resource.Single(x => x.Type == systemComponent.Type); // If the system has been set to support include, then build the possible include params from the search parameters if (system.SupportsInclude) { systemComponent.SearchInclude = systemComponent.SearchParam.Where(sp => sp.Type == SearchParamType.Reference).Select(sp => $"{systemComponent.Type.ToString()}.{sp.Name}").ToList(); } var interaction = new CapabilityStatement.ResourceComponent { // System predefined values Type = systemComponent.Type, // User configurable override Profile = configuredComponent.Profile ?? systemComponent.Profile, // Boolean intersections ReadHistory = systemComponent.ReadHistory.IntersectBool(configuredComponent.ReadHistory, issues, $"Rest.Resource['{systemComponent.Type}'].ReadHistory"), UpdateCreate = systemComponent.UpdateCreate.IntersectBool(configuredComponent.UpdateCreate, issues, $"Rest.Resource['{systemComponent.Type}'.UpdateCreate"), ConditionalCreate = systemComponent.ConditionalCreate.IntersectBool(configuredComponent.ConditionalCreate, issues, $"Rest.Resource['{systemComponent.Type}'].ConditionalCreate"), ConditionalUpdate = systemComponent.ConditionalUpdate.IntersectBool(configuredComponent.ConditionalUpdate, issues, $"Rest.Resource['{systemComponent.Type}'].ConditionalUpdate"), // List intersections SearchInclude = system.SupportsInclude ? systemComponent.SearchInclude.IntersectList(configuredComponent.SearchInclude, x => x, issues, $"Rest.Resource['{systemComponent.Type}'].SearchInclude").ToList() : Enumerable.Empty <string>(), SearchRevInclude = systemComponent.SearchRevInclude.IntersectList(configuredComponent.SearchRevInclude, x => x, issues, $"Rest.Resource['{systemComponent.Type}'].SearchRevInclude").ToList(), Interaction = systemComponent.Interaction.IntersectList(configuredComponent.Interaction, x => x.Code, issues, $"Rest.Resource['{systemComponent.Type}'].Interaction"), ReferencePolicy = systemComponent.ReferencePolicy.IntersectList(configuredComponent.ReferencePolicy, x => x, issues, $"Rest.Resource['{systemComponent.Type}'].ReferencePolicy"), SearchParam = systemComponent.SearchParam.IntersectList(configuredComponent.SearchParam, x => string.Concat(x.Name, x.Type), issues, $"Rest.Resource['{systemComponent.Type}'].SearchParam"), // Listed Enumerations intersections Versioning = systemComponent.Versioning.IntersectEnum(configuredComponent.Versioning, issues, $"Rest.Resource['{systemComponent.Type}'].Versioning"), ConditionalRead = systemComponent.ConditionalRead.IntersectEnum(configuredComponent.ConditionalRead, issues, $"Rest.Resource['{systemComponent.Type}'].ConditionalRead"), ConditionalDelete = systemComponent.ConditionalDelete.IntersectEnum(configuredComponent.ConditionalDelete, issues, $"Rest.Resource['{systemComponent.Type}'].ConditionalDelete"), }; rest.Resource.Add(interaction); } rest.Resource = rest.Resource.OrderBy(x => x.Type.ToString()).ToList(); } if (strictConfig && issues.Any()) { throw new UnsupportedConfigurationException(Core.Resources.UnsupportedConfigurationMessage, issues.Select(i => new OperationOutcomeIssue(OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, i)).ToArray()); } return(intersecting); }
public static ListedCapabilityStatement AddOAuthSecurityService(this ListedCapabilityStatement statement, string authority, IHttpClientFactory httpClientFactory, ILogger logger) { EnsureArg.IsNotNull(statement, nameof(statement)); EnsureArg.IsNotNull(authority, nameof(authority)); EnsureArg.IsNotNull(httpClientFactory, nameof(httpClientFactory)); var restComponent = statement.GetListedRestComponent(); var security = restComponent.Security ?? new CapabilityStatement.SecurityComponent(); security.Service.Add(Constants.RestfulSecurityServiceOAuth); var openIdConfigurationUrl = $"{authority}/.well-known/openid-configuration"; HttpResponseMessage openIdConfigurationResponse; using (var httpClient = httpClientFactory.CreateClient()) { try { openIdConfigurationResponse = httpClient.GetAsync(new Uri(openIdConfigurationUrl)).GetAwaiter().GetResult(); } catch (Exception ex) { logger.LogWarning(ex, $"There was an exception while attempting to read the OpenId Configuration from \"{openIdConfigurationUrl}\"."); throw new OpenIdConfigurationException(); } } if (openIdConfigurationResponse.IsSuccessStatusCode) { var openIdConfiguration = JObject.Parse(openIdConfigurationResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult()); string tokenEndpoint, authorizationEndpoint; try { tokenEndpoint = openIdConfiguration["token_endpoint"].Value <string>(); authorizationEndpoint = openIdConfiguration["authorization_endpoint"].Value <string>(); } catch (Exception ex) { logger.LogWarning(ex, $"There was an exception while attempting to read the endpoints from \"{openIdConfigurationUrl}\"."); throw new OpenIdConfigurationException(); } var smartExtension = new Extension() { Url = Constants.SmartOAuthUriExtension, Extension = new List <Extension> { new Extension(Constants.SmartOAuthUriExtensionToken, new FhirUri(tokenEndpoint)), new Extension(Constants.SmartOAuthUriExtensionAuthorize, new FhirUri(authorizationEndpoint)), }, }; security.Extension.Add(smartExtension); } else { logger.LogWarning($"The OpenId Configuration request from \"{openIdConfigurationUrl}\" returned an {openIdConfigurationResponse.StatusCode} status code."); throw new OpenIdConfigurationException(); } restComponent.Security = security; return(statement); }