private static void CreateTags(IList <HttpFunctionDefinition> functionDefinitions, IOpenApiHttpFunctionFilter functionFilter, OpenApiDocument openApiDocument) { IOpenApiHttpFunctionFilterContext functionFilterContext = new OpenApiHttpFunctionFilterContext(); HashSet <HttpRouteConfiguration> routeConfigurations = new HashSet <HttpRouteConfiguration>(); foreach (HttpFunctionDefinition functionDefinition in functionDefinitions) { if (functionDefinition.OpenApiIgnore) { continue; } if (!string.IsNullOrWhiteSpace(functionDefinition.RouteConfiguration?.OpenApiName)) { var filterdVerbs = new HashSet <HttpMethod>(functionDefinition.Verbs); functionFilter.Apply(functionDefinition.RouteConfiguration.Route, filterdVerbs, functionFilterContext); if (filterdVerbs.Count != 0) { routeConfigurations.Add(functionDefinition.RouteConfiguration); } } } if (routeConfigurations.Count == 0) { return; } openApiDocument.Tags = routeConfigurations.Select(x => new OpenApiTag { Description = x.OpenApiDescription, Name = x.OpenApiName }).ToList(); }
private void CreateSchemas(IList <HttpFunctionDefinition> functionDefinitions, IOpenApiHttpFunctionFilter functionFilter, OpenApiDocument openApiDocument, SchemaReferenceRegistry registry) { IOpenApiHttpFunctionFilterContext functionFilterContext = new OpenApiHttpFunctionFilterContext(); foreach (HttpFunctionDefinition functionDefinition in functionDefinitions) { if (functionDefinition.OpenApiIgnore) { continue; } var filterdVerbs = new HashSet <HttpMethod>(functionDefinition.Verbs); functionFilter.Apply(functionDefinition.RouteConfiguration.Route, filterdVerbs, functionFilterContext); if (filterdVerbs.Count == 0) { continue; } if (filterdVerbs.Contains(HttpMethod.Patch) || filterdVerbs.Contains(HttpMethod.Post) || filterdVerbs.Contains(HttpMethod.Put)) { registry.FindOrAddReference(functionDefinition.CommandType); } if (functionDefinition.CommandResultType != null && functionDefinition.CommandResultType != typeof(IActionResult)) { registry.FindOrAddReference(functionDefinition.CommandResultType); } } openApiDocument.Components.Schemas = registry.References; }
private static void CreateOperationsFromRoutes( IList <HttpFunctionDefinition> functionDefinitions, IOpenApiHttpFunctionFilter functionFilter, OpenApiDocument openApiDocument, SchemaReferenceRegistry registry, string apiPrefix, OpenApiCompilerConfiguration compilerConfiguration) { IOpenApiHttpFunctionFilterContext functionFilterContext = new OpenApiHttpFunctionFilterContext(); string prependedApiPrefix = string.IsNullOrEmpty(apiPrefix) ? $"" : $"/{apiPrefix}"; var operationsByRoute = functionDefinitions.Where(x => x.Route != null).GroupBy(x => $"{prependedApiPrefix}/{x.Route}"); foreach (IGrouping <string, HttpFunctionDefinition> route in operationsByRoute) { OpenApiPathItem pathItem = new OpenApiPathItem() { Operations = new Dictionary <OperationType, OpenApiOperation>() }; foreach (HttpFunctionDefinition functionByRoute in route) { if (functionByRoute.OpenApiIgnore) { continue; } var filterdVerbs = new HashSet <HttpMethod>(functionByRoute.Verbs); functionFilter.Apply(functionByRoute.RouteConfiguration.Route, filterdVerbs, functionFilterContext); if (filterdVerbs.Count == 0) { continue; } Type commandType = functionByRoute.CommandType; foreach (HttpMethod method in filterdVerbs) { OpenApiOperation operation = new OpenApiOperation { Description = functionByRoute.OpenApiDescription, Summary = functionByRoute.OpenApiSummary, Responses = new OpenApiResponses(), Tags = string.IsNullOrWhiteSpace(functionByRoute.RouteConfiguration.OpenApiName) ? null : new List <OpenApiTag>() { new OpenApiTag { Name = functionByRoute.RouteConfiguration.OpenApiName } } }; var operationFilterContext = new OpenApiOperationFilterContext { CommandType = commandType, PropertyNames = new Dictionary <string, string>() }; foreach (KeyValuePair <int, OpenApiResponseConfiguration> kvp in functionByRoute.OpenApiResponseConfigurations) { operation.Responses.Add(kvp.Key.ToString(), new OpenApiResponse { Description = kvp.Value.Description, Content = { ["application/json"] = new OpenApiMediaType() { Schema = kvp.Value.ResponseType == null ? null : registry.FindOrAddReference(kvp.Value.ResponseType) } } }); } // Does any HTTP success response (2xx) exist if (operation.Responses.Keys.FirstOrDefault(x => x.StartsWith("2")) == null) { OpenApiResponse response = new OpenApiResponse { Description = "Successful API operation" }; if (functionByRoute.CommandResultType != null) { OpenApiSchema schema = registry.FindOrAddReference(functionByRoute.CommandResultType); response.Content = new Dictionary <string, OpenApiMediaType> { { "application/json", new OpenApiMediaType { Schema = schema } } }; } operation.Responses.Add("200", response); } if (method == HttpMethod.Get || method == HttpMethod.Delete) { var schema = registry.GetOrCreateSchema(commandType); foreach (HttpParameter property in functionByRoute.QueryParameters) { var propertyInfo = commandType.GetProperty(property.Name); // Property Name var propertyName = propertyInfo.GetAttributeValue((JsonPropertyAttribute attribute) => attribute.PropertyName); if (string.IsNullOrWhiteSpace(propertyName)) { propertyName = propertyInfo.GetAttributeValue((DataMemberAttribute attribute) => attribute.Name); } if (string.IsNullOrWhiteSpace(propertyName)) { propertyName = propertyInfo.Name.ToCamelCase(); } // Property Required var propertyRequired = !property.IsOptional; if (!propertyRequired) { propertyRequired = propertyInfo.GetAttributeValue((JsonPropertyAttribute attribute) => attribute.Required) == Required.Always; } if (!propertyRequired) { propertyRequired = propertyInfo.GetAttributeValue((RequiredAttribute attribute) => attribute) != null; } var propertySchema = schema.Properties[propertyName]; var parameter = new OpenApiParameter { Name = propertyName, In = ParameterLocation.Query, Required = propertyRequired, Schema = propertySchema, // property.Type.MapToOpenApiSchema(), Description = propertySchema.Description }; FilterParameter(compilerConfiguration.ParameterFilters, parameter); operation.Parameters.Add(parameter); operationFilterContext.PropertyNames[parameter.Name] = propertyInfo.Name; } } if (functionByRoute.Authorization == AuthorizationTypeEnum.Function && (method == HttpMethod.Get || method == HttpMethod.Delete)) { operation.Parameters.Add(new OpenApiParameter { Name = "code", In = ParameterLocation.Query, Required = true, Schema = typeof(string).MapToOpenApiSchema(), Description = "" }); } // TODO: FIXME: // Empty OpenApiSecurityRequirement lists are not serialized by the standard Microsoft // implementation. Therefore we add a null object to the list here and fix it later by hand. if (functionByRoute.Authorization == AuthorizationTypeEnum.Anonymous) { operation.Security.Add(null); } foreach (HttpParameter property in functionByRoute.RouteParameters) { var parameter = new OpenApiParameter { Name = property.RouteName.ToCamelCase(), In = ParameterLocation.Path, Required = !property.IsOptional, Schema = property.Type.MapToOpenApiSchema(), Description = "" }; FilterParameter(compilerConfiguration.ParameterFilters, parameter); operation.Parameters.Add(parameter); // TODO: We need to consider what to do with the payload model here - if its a route parameter // we need to ignore it in the payload model } if (method == HttpMethod.Post || method == HttpMethod.Put || method == HttpMethod.Patch) { OpenApiRequestBody requestBody = new OpenApiRequestBody(); OpenApiSchema schema = registry.FindReference(commandType); requestBody.Content = new Dictionary <string, OpenApiMediaType> { { "application/json", new OpenApiMediaType { Schema = schema } } }; operation.RequestBody = requestBody; } FilterOperation(compilerConfiguration.OperationFilters, operation, operationFilterContext); pathItem.Operations.Add(MethodToOperationMap[method], operation); } } if (pathItem.Operations.Count != 0) { openApiDocument.Paths.Add(route.Key, pathItem); } } }