private AspNetCoreOperationProcessorContext GetContext(ApiDescription apiDescription) { var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; var swaggerSettings = new AspNetCoreOpenApiDocumentGeneratorSettings(); var document = new OpenApiDocument(); var schemaGeneratorSettings = new OpenApiDocumentGeneratorSettings(); var schemaGenerator = new OpenApiSchemaGenerator(schemaGeneratorSettings); var schemaResolver = new OpenApiSchemaResolver(document, schemaGeneratorSettings); swaggerSettings.SchemaGenerator = schemaGenerator; var context = new AspNetCoreOperationProcessorContext( document, operationDescription, GetType(), GetType().GetMethod(nameof(SomeAction), BindingFlags.NonPublic | BindingFlags.Instance), new OpenApiDocumentGenerator(swaggerSettings, schemaResolver), schemaGenerator, schemaResolver, swaggerSettings, new List <OpenApiOperationDescription>()) { ApiDescription = apiDescription, }; return(context); }
private void ProcessSwaggerTagsAttribute(OpenApiDocument document, OpenApiOperationDescription operationDescription, MethodInfo methodInfo) { dynamic tagsAttribute = methodInfo .GetCustomAttributes() .FirstAssignableToTypeNameOrDefault("SwaggerTagsAttribute", TypeNameStyle.Name); if (tagsAttribute != null) { var tags = ((string[])tagsAttribute.Tags).ToList(); foreach (var tag in tags) { if (operationDescription.Operation.Tags.All(t => t != tag)) { operationDescription.Operation.Tags.Add(tag); } if (ObjectExtensions.HasProperty(tagsAttribute, "AddToDocument") && tagsAttribute.AddToDocument) { if (document.Tags == null) { document.Tags = new List <OpenApiTag>(); } if (document.Tags.All(t => t.Name != tag)) { document.Tags.Add(new OpenApiTag { Name = tag }); } } } } }
private async Task <bool> GenerateForAzureFunctionClassAsync(OpenApiDocument document, Type staticAzureFunctionClassType, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver, IList <string> functionNames) { var operations = new List <Tuple <OpenApiOperationDescription, MethodInfo> >(); foreach (var method in GetActionMethods(staticAzureFunctionClassType, functionNames)) { var httpPaths = GetHttpPaths(method); var httpMethods = GetSupportedHttpMethods(method); foreach (var httpPath in httpPaths) { foreach (var httpMethod in httpMethods) { var operationDescription = new OpenApiOperationDescription { Path = httpPath, Method = httpMethod, Operation = new OpenApiOperation { IsDeprecated = method.GetCustomAttribute <ObsoleteAttribute>() != null, OperationId = GetOperationId(document, staticAzureFunctionClassType.Name, method) } }; operations.Add(new Tuple <OpenApiOperationDescription, MethodInfo>(operationDescription, method)); } } } return(await AddOperationDescriptionsToDocumentAsync(document, staticAzureFunctionClassType, operations, swaggerGenerator, schemaResolver)); }
private void EnsureSingleBodyParameter(OpenApiOperationDescription operationDescription) { if (operationDescription.Operation.ActualParameters.Count(p => p.Kind == OpenApiParameterKind.Body) > 1) { throw new InvalidOperationException("The operation '" + operationDescription.Operation.OperationId + "' has more than one body parameter."); } }
private void ApplyOpenApiBodyParameterAttribute(OpenApiOperationDescription operationDescription, MethodInfo methodInfo) { dynamic bodyParameterAttribute = methodInfo.GetCustomAttributes() .FirstAssignableToTypeNameOrDefault("OpenApiBodyParameterAttribute", TypeNameStyle.Name); if (bodyParameterAttribute != null) { if (operationDescription.Operation.RequestBody == null) { operationDescription.Operation.RequestBody = new OpenApiRequestBody(); } var mimeTypes = ObjectExtensions.HasProperty(bodyParameterAttribute, "MimeType") ? new string[] { bodyParameterAttribute.MimeType } : bodyParameterAttribute.MimeTypes; foreach (var mimeType in mimeTypes) { operationDescription.Operation.RequestBody.Content[mimeType] = new OpenApiMediaType { Schema = mimeType == "application/json" ? JsonSchema.CreateAnySchema() : new JsonSchema { Type = _settings.SchemaType == SchemaType.Swagger2 ? JsonObjectType.File : JsonObjectType.String, Format = _settings.SchemaType == SchemaType.Swagger2 ? null : JsonFormatStrings.Binary, } }; } } }
private void UpdateConsumedTypes(OpenApiOperationDescription operationDescription) { if (operationDescription.Operation.ActualParameters.Any(p => p.IsBinary || p.ActualSchema.IsBinary)) { operationDescription.Operation.TryAddConsumes("multipart/form-data"); } }
public void ProcessAsync_Adds200StatusCodeForVoidResponse() { // Arrange var apiDescription = new ApiDescription { SupportedResponseTypes = { new ApiResponseType { Type = typeof(void), StatusCode = 0, } } }; var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; var context = GetContext(apiDescription); var processor = new OperationResponseProcessor((AspNetCoreOpenApiDocumentGeneratorSettings)context.Settings); // Act processor.Process(context); // Assert Assert.Collection( context.OperationDescription.Operation.Responses, kvp => { Assert.Equal("200", kvp.Key); Assert.Null(kvp.Value.Schema); }); }
/// <exception cref="InvalidOperationException">The operation has more than one body parameter.</exception> private bool GenerateForController(OpenApiDocument document, Type controllerType, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) { var hasIgnoreAttribute = controllerType.GetTypeInfo() .GetCustomAttributes() .GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name) .Any(); if (hasIgnoreAttribute) { return(false); } var operations = new List <Tuple <OpenApiOperationDescription, MethodInfo> >(); var currentControllerType = controllerType; while (currentControllerType != null) { foreach (var method in GetActionMethods(currentControllerType)) { var httpPaths = GetHttpPaths(controllerType, method).ToList(); var httpMethods = GetSupportedHttpMethods(method).ToList(); foreach (var httpPath in httpPaths) { foreach (var httpMethod in httpMethods) { var isPathAlreadyDefinedInInheritanceHierarchy = operations.Any(o => o.Item1.Path == httpPath && o.Item1.Method == httpMethod && o.Item2.DeclaringType != currentControllerType && o.Item2.DeclaringType.IsAssignableToTypeName(currentControllerType.FullName, TypeNameStyle.FullName)); if (isPathAlreadyDefinedInInheritanceHierarchy == false) { var operationDescription = new OpenApiOperationDescription { Path = httpPath, Method = httpMethod, Operation = new OpenApiOperation { IsDeprecated = method.GetCustomAttribute <ObsoleteAttribute>() != null, OperationId = GetOperationId(document, controllerType.Name, method, httpMethod) } }; operations.Add(new Tuple <OpenApiOperationDescription, MethodInfo>(operationDescription, method)); } } } } currentControllerType = currentControllerType.GetTypeInfo().BaseType; } return(AddOperationDescriptionsToDocument(document, controllerType, operations, swaggerGenerator, schemaResolver)); }
private OperationProcessorContext GetContext(Type controllerType, MethodInfo methodInfo) { var document = new OpenApiDocument(); var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; return(new OperationProcessorContext(document, operationDescription, controllerType, methodInfo, null, null, null, null, null)); }
/// <summary> /// Sets the IsNullableRaw property of parameters to null for OpenApi3 schemas. /// </summary> /// <param name="operationDescription">Operation to check.</param> /// <param name="schemaType">Schema type.</param> private void UpdateNullableRawOperationParameters(OpenApiOperationDescription operationDescription, SchemaType schemaType) { if (schemaType == SchemaType.OpenApi3) { foreach (OpenApiParameter openApiParameter in operationDescription.Operation.Parameters) { openApiParameter.IsNullableRaw = null; } } }
private static string GetPathExpression(OpenApiOperationDescription operation) { var pathExpression = operation.Path; if (pathExpression.StartsWith("/", StringComparison.InvariantCulture)) { pathExpression = pathExpression.Substring(1); } pathExpression = pathExpression.Replace("{namespace}", "{namespaceParameter}"); return(pathExpression); }
private OperationProcessorContext GetContext(Type controllerType, MethodInfo methodInfo) { var document = new OpenApiDocument(); var operationDescription = new OpenApiOperationDescription { Operation = new OpenApiOperation() }; var settings = new OpenApiDocumentGeneratorSettings { UseControllerSummaryAsTagDescription = true }; return(new OperationProcessorContext(document, operationDescription, controllerType, methodInfo, null, null, null, settings, null)); }
private void RemoveUnusedPathParameters(OpenApiOperationDescription operationDescription, string httpPath) { operationDescription.Path = Regex.Replace(httpPath, "{(.*?)(:(([^/]*)?))?}", match => { var parameterName = match.Groups[1].Value.TrimEnd('?'); if (operationDescription.Operation.ActualParameters.Any(p => p.Kind == OpenApiParameterKind.Path && string.Equals(p.Name, parameterName, StringComparison.OrdinalIgnoreCase))) { return("{" + parameterName + "}"); } return(string.Empty); }).TrimEnd('/'); }
private void ProcessControllerSwaggerTagAttributes(OpenApiOperationDescription operationDescription, TypeInfo typeInfo) { foreach (var tagAttribute in typeInfo.GetCustomAttributes() .GetAssignableToTypeName("OpenApiTagAttribute", TypeNameStyle.Name) .Select(a => (dynamic)a)) { if (operationDescription.Operation.Tags.All(t => t != tagAttribute.Name)) { operationDescription.Operation.Tags.Add(tagAttribute.Name); } } }
/// <summary>Initializes a new instance of the <see cref="AspNetCoreOperationProcessorContext" /> class.</summary> /// <param name="document">The document.</param> /// <param name="operationDescription">The operation description.</param> /// <param name="controllerType">Type of the controller.</param> /// <param name="methodInfo">The method information.</param> /// <param name="swaggerGenerator">The swagger generator.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <param name="settings">The sett</param> /// <param name="allOperationDescriptions">All operation descriptions.</param> /// <param name="schemaGenerator">The schema generator.</param> public AspNetCoreOperationProcessorContext( OpenApiDocument document, OpenApiOperationDescription operationDescription, Type controllerType, MethodInfo methodInfo, OpenApiDocumentGenerator swaggerGenerator, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver, OpenApiDocumentGeneratorSettings settings, IList <OpenApiOperationDescription> allOperationDescriptions) : base(document, operationDescription, controllerType, methodInfo, swaggerGenerator, schemaGenerator, schemaResolver, settings, allOperationDescriptions) { }
/// <summary> /// Map ApiOperation to TypeScriptOperationModel /// </summary> /// <param name="openApiOperation"></param> /// <returns></returns> public virtual TypeScriptOperationModel GetOperationModelByApiOperation( OpenApiOperationDescription openApiOperation) { var operationModel = new TypeScriptOperationModel(openApiOperation.Operation, _clientGeneratorSettings, _typeScriptClientGenerator, _resolver); operationModel.ControllerName = _clientGeneratorSettings.OperationNameGenerator.GetClientName( _openApiDocument, openApiOperation.Path, openApiOperation.Method, openApiOperation.Operation); operationModel.Path = openApiOperation.Path; operationModel.HttpMethod = openApiOperation.Method; operationModel.OperationName = _clientGeneratorSettings.OperationNameGenerator.GetOperationName( _openApiDocument, openApiOperation.Path, openApiOperation.Method, openApiOperation.Operation); return(operationModel); }
public async Task When_complex_type_with_nullable_primitive_query_parameter_exists_then_parameter_IsNullableRaw_is_null() { //// Arrange WebApiOpenApiDocumentGenerator generator = this.GetOpenApi3Generator(); //// Act OpenApiDocument document = await generator.GenerateForControllerAsync <TestController>(); OpenApiOperationDescription operationDescription = document.Operations.Single(o => o.Operation.OperationId.EndsWith(nameof(TestController.GetByReferenceTypeWithNullablePrimitiveType))); OpenApiOperation operation = operationDescription.Operation; //// Assert Assert.IsNull(operation.Parameters.Single().IsNullableRaw); }
private void ProcessSwaggerTagAttributes(OpenApiDocument document, OpenApiOperationDescription operationDescription, MethodInfo methodInfo) { foreach (var tagAttribute in methodInfo.GetCustomAttributes() .GetAssignableToTypeName("SwaggerTagAttribute", TypeNameStyle.Name) .Select(a => (dynamic)a)) { if (operationDescription.Operation.Tags.All(t => t != tagAttribute.Name)) { operationDescription.Operation.Tags.Add(tagAttribute.Name); } if (ObjectExtensions.HasProperty(tagAttribute, "AddToDocument") && tagAttribute.AddToDocument) { DocumentTagsProcessor.ProcessTagAttribute(document, tagAttribute); } } }
private void UpdateConsumedTypes(OpenApiOperationDescription operationDescription) { if (operationDescription.Operation.ActualParameters.Any(p => p.Type == JsonObjectType.File)) { operationDescription.Operation.Consumes = new List <string> { "multipart/form-data" } } ; else if (operationDescription.Operation.ActualParameters.Any(p => p.Kind == OpenApiParameterKind.FormData)) { operationDescription.Operation.Consumes = new List <string>() { "application/x-www-form-urlencoded" } } ; }
/// <summary>Gets the security scopes for an operation.</summary> /// <param name="operationDescription">The operation description.</param> /// <param name="methodInfo">The method information.</param> /// <returns>The scopes.</returns> protected virtual IEnumerable <string> GetScopes(OpenApiOperationDescription operationDescription, MethodInfo methodInfo) { var allAttributes = methodInfo.GetCustomAttributes().Concat( methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes()); var authorizeAttributes = allAttributes.Where(a => a.GetType().Name == "AuthorizeAttribute").ToList(); if (!authorizeAttributes.Any()) { return(Enumerable.Empty <string>()); } return(authorizeAttributes .Select(a => (dynamic)a) .Where(a => a.Roles != null) .SelectMany(a => ((string)a.Roles).Split(',')) .Distinct()); }
private void ProcessControllerSwaggerTagsAttribute(OpenApiOperationDescription operationDescription, TypeInfo typeInfo) { dynamic tagsAttribute = typeInfo .GetCustomAttributes() .FirstAssignableToTypeNameOrDefault("OpenApiTagsAttribute", TypeNameStyle.Name); if (tagsAttribute != null) { var tags = ((string[])tagsAttribute.Tags).ToList(); foreach (var tag in tags) { if (operationDescription.Operation.Tags.All(t => t != tag)) { operationDescription.Operation.Tags.Add(tag); } } } }
private void AddOperation(Domain domain, OpenApiOperationDescription op, OpenApiDocument doc) { if (!OperationProducesBinary(op)) { Log.Information("Adding operation {Path} to domain", op.Path); var parts = DecomposeRoutePath(op.Path); var noun = Util.CanonicalizeName(parts.noun); var relatedType = domain.Types.FirstOrDefault(t => Util.CanonicalizeName(t.Name) == noun); var domainOp = new Operation { Name = $"{ConvertApiStyleNameToStandardName(parts.noun)}_{ConvertApiStyleNameToStandardName(parts.verb)}", Namespace = domain.DefaultNamespace, RelatedType = relatedType }; domainOp.Attributes = new JObject(); domainOp.Attributes.HttpMethod = op.Method; if (op.Method == "post" || op.Method == "put") { domainOp.Attributes.changesData = true; domainOp.Attributes.createsNew = true; // don't do a 'select by Id' first } if (op.Method == "delete") { domainOp.Attributes.isDelete = true; } PopulateNewOperationParameters(domain, op, doc, domainOp); if (op.Operation.ActualResponses.ContainsKey("200")) { PopulateNewOperationResponse(domain, op, doc, domainOp); } else { Log.Information("Operation {Path} does not define a successful response, so no domain operation will be added for it.", op.Path); } domain.Operations.Add(domainOp); } else { Log.Debug("OpenApi operation {Path} produces a file. We won't add operation information for it.", op.Path); } }
private void ApplyOpenApiBodyParameterAttribute(OpenApiOperationDescription operationDescription, MethodInfo methodInfo) { dynamic bodyParameterAttribute = methodInfo.GetCustomAttributes() .FirstAssignableToTypeNameOrDefault("OpenApiBodyParameterAttribute", TypeNameStyle.Name); if (bodyParameterAttribute != null) { if (operationDescription.Operation.RequestBody == null) { operationDescription.Operation.RequestBody = new OpenApiRequestBody(); } operationDescription.Operation.RequestBody.Content[bodyParameterAttribute.MimeType] = new OpenApiMediaType { Schema = bodyParameterAttribute.MimeType == "application/json" ? JsonSchema.CreateAnySchema() : new JsonSchema { Type = JsonObjectType.File } }; } }
private bool GenerateForHandler(OpenApiDocument document, Type handlerType, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) { var methodInfo = handlerType.GetMethod("Handle"); var isQuery = handlerType.IsAssignableToGenericType(typeof(IQueryRequestHandler <,>)); var httpMethod = isQuery ? OpenApiOperationMethod.Get : OpenApiOperationMethod.Post; var httpPath = $"/rpc/{GetRequestTypeFullName(methodInfo)}"; var operationDescription = new OpenApiOperationDescription { Path = httpPath, Method = httpMethod, Operation = new OpenApiOperation { IsDeprecated = methodInfo.GetCustomAttribute <ObsoleteAttribute>() != null, OperationId = GetOperationId(document, handlerType.Name) } }; return(AddOperationDescriptionsToDocumentAsync(document, handlerType, (operationDescription, methodInfo), swaggerGenerator, schemaResolver)); }
private void PopulateNewOperationParameters(Domain domain, OpenApiOperationDescription op, OpenApiDocument doc, Operation domainOp) { var index = 0; foreach (var apiParameter in op.Operation.ActualParameters) { var clrType = GetClrTypeForJsonSchemaType(apiParameter.Schema.Type); if (clrType == null) { // TODO - find or create a domain type to match this } var parameter = new Parameter(domain, domainOp) { Name = apiParameter.Name, ClrType = clrType, Order = index, Attributes = new JObject() }; parameter.Attributes.userEditable = true; domainOp.Parameters.Add(parameter); index++; Log.Debug("Parameter: {param}", apiParameter); } }
// TODO: remove asyncness as most NSwag operations have been converted to sync. private async Task <bool> RunOperationProcessorsAsync(OpenApiDocument document, Type staticAzureFunctionClassType, MethodInfo methodInfo, OpenApiOperationDescription operationDescription, List <OpenApiOperationDescription> allOperations, OpenApiDocumentGenerator swaggerGenerator, OpenApiSchemaResolver schemaResolver) { var context = new OperationProcessorContext(document, operationDescription, staticAzureFunctionClassType, methodInfo, swaggerGenerator, Settings.SchemaGenerator, schemaResolver, Settings, allOperations); // 1. Run from settings foreach (var operationProcessor in Settings.OperationProcessors) { if (operationProcessor.Process(context) == false) { return(false); } } // 2. Run from class attributes var operationProcessorAttribute = methodInfo.DeclaringType.GetTypeInfo() .GetCustomAttributes() // 3. Run from method attributes .Concat(methodInfo.GetCustomAttributes()) .Where(a => a.GetType().IsAssignableToTypeName("SwaggerOperationProcessorAttribute", TypeNameStyle.Name)); foreach (dynamic attribute in operationProcessorAttribute) { var operationProcessor = ObjectExtensions.HasProperty(attribute, "Parameters") ? (IOperationProcessor)Activator.CreateInstance(attribute.Type, attribute.Parameters) : (IOperationProcessor)Activator.CreateInstance(attribute.Type); if (operationProcessor.Process(context) == false) { return(false); } } return(true); }
/// <summary>Initializes a new instance of the <see cref="OperationProcessorContext" /> class.</summary> /// <param name="document">The document.</param> /// <param name="operationDescription">The operation description.</param> /// <param name="controllerType">Type of the controller.</param> /// <param name="methodInfo">The method information.</param> /// <param name="openApiDocumentGenerator">The swagger generator.</param> /// <param name="schemaResolver">The schema resolver.</param> /// <param name="settings">The settings.</param> /// <param name="allOperationDescriptions">All operation descriptions.</param> /// <param name="schemaGenerator">The schema generator.</param> public OperationProcessorContext( OpenApiDocument document, OpenApiOperationDescription operationDescription, Type controllerType, MethodInfo methodInfo, OpenApiDocumentGenerator openApiDocumentGenerator, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver, OpenApiDocumentGeneratorSettings settings, IList <OpenApiOperationDescription> allOperationDescriptions) { Document = document; OperationDescription = operationDescription; ControllerType = controllerType; MethodInfo = methodInfo; DocumentGenerator = openApiDocumentGenerator; SchemaGenerator = schemaGenerator; SchemaResolver = schemaResolver; Settings = settings; AllOperationDescriptions = allOperationDescriptions; }
private List <Type> GenerateApiGroups( OpenApiDocument document, IGrouping <Type, Tuple <ApiDescription, ActionDescriptor> >[] apiGroups, OpenApiSchemaResolver schemaResolver) { var usedControllerTypes = new List <Type>(); var swaggerGenerator = new OpenApiDocumentGenerator(Settings, schemaResolver); var allOperations = new List <Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo> >(); foreach (var apiGroup in apiGroups) { var controllerType = apiGroup.Key; var hasIgnoreAttribute = controllerType != null && controllerType .GetTypeInfo() .GetCustomAttributes() .GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name) .Any(); if (!hasIgnoreAttribute) { var operations = new List <Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo> >(); foreach (var item in apiGroup) { var apiDescription = item.Item1; if (apiDescription.RelativePath == null) { continue; } var method = (item.Item2 as ControllerActionDescriptor)?.MethodInfo; if (method != null) { var actionHasIgnoreAttribute = method.GetCustomAttributes().GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name).Any(); if (actionHasIgnoreAttribute) { continue; } } var path = apiDescription.RelativePath; if (!path.StartsWith("/", StringComparison.Ordinal)) { path = "/" + path; } var httpMethod = apiDescription.HttpMethod?.ToLowerInvariant() ?? (apiDescription.ParameterDescriptions.Where(p => { return(p.Source == Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource.Body); }).Count() > 0 ? OpenApiOperationMethod.Post : OpenApiOperationMethod.Get); var operationDescription = new OpenApiOperationDescription { Path = path, Method = httpMethod, Operation = new OpenApiOperation { IsDeprecated = IsOperationDeprecated(item.Item1, apiDescription.ActionDescriptor, method), OperationId = GetOperationId(document, apiDescription, method, httpMethod), Consumes = apiDescription.SupportedRequestFormats .Select(f => f.MediaType) .Distinct() .ToList(), Produces = apiDescription.SupportedResponseTypes .SelectMany(t => t.ApiResponseFormats.Select(f => f.MediaType)) .Distinct() .ToList() } }; operations.Add(new Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo>(operationDescription, apiDescription, method)); } var addedOperations = AddOperationDescriptionsToDocument(document, controllerType, operations, swaggerGenerator, schemaResolver); if (addedOperations.Any() && apiGroup.Key != null) { usedControllerTypes.Add(apiGroup.Key); } allOperations.AddRange(addedOperations); } } UpdateConsumesAndProduces(document, allOperations); return(usedControllerTypes); }
private List <Type> GenerateForControllers( OpenApiDocument document, IGrouping <Type, Tuple <ApiDescription, ControllerActionDescriptor> >[] apiGroups, OpenApiSchemaResolver schemaResolver) { var usedControllerTypes = new List <Type>(); var swaggerGenerator = new OpenApiDocumentGenerator(Settings, schemaResolver); var allOperations = new List <Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo> >(); foreach (var controllerApiDescriptionGroup in apiGroups) { var controllerType = controllerApiDescriptionGroup.Key; var hasIgnoreAttribute = controllerType.GetTypeInfo() .GetCustomAttributes() .GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name) .Any(); if (!hasIgnoreAttribute) { var operations = new List <Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo> >(); foreach (var item in controllerApiDescriptionGroup) { var apiDescription = item.Item1; var method = item.Item2.MethodInfo; var actionHasIgnoreAttribute = method.GetCustomAttributes().GetAssignableToTypeName("SwaggerIgnoreAttribute", TypeNameStyle.Name).Any(); if (actionHasIgnoreAttribute) { continue; } var path = apiDescription.RelativePath; if (!path.StartsWith("/", StringComparison.Ordinal)) { path = "/" + path; } var controllerActionDescriptor = (ControllerActionDescriptor)apiDescription.ActionDescriptor; var httpMethod = apiDescription.HttpMethod?.ToLowerInvariant() ?? OpenApiOperationMethod.Get; var operationDescription = new OpenApiOperationDescription { Path = path, Method = httpMethod, Operation = new OpenApiOperation { IsDeprecated = method.GetCustomAttribute <ObsoleteAttribute>() != null, OperationId = GetOperationId(document, controllerActionDescriptor, method), Consumes = apiDescription.SupportedRequestFormats .Select(f => f.MediaType) .Distinct() .ToList(), Produces = apiDescription.SupportedResponseTypes .SelectMany(t => t.ApiResponseFormats.Select(f => f.MediaType)) .Distinct() .ToList() } }; operations.Add(new Tuple <OpenApiOperationDescription, ApiDescription, MethodInfo>(operationDescription, apiDescription, method)); } var addedOperations = AddOperationDescriptionsToDocument(document, controllerType, operations, swaggerGenerator, schemaResolver); if (addedOperations.Any()) { usedControllerTypes.Add(controllerApiDescriptionGroup.Key); } allOperations.AddRange(addedOperations); } } UpdateConsumesAndProduces(document, allOperations); return(usedControllerTypes); }
private (Operation operation, MatchType matchType) FindMatchingDomainOperation(Domain domain, OpenApiOperationDescription op, OpenApiDocument doc) { string noun = null; string verb = null; try { var routeParts = DecomposeRoutePath(op.Path); noun = Util.CanonicalizeName(routeParts.noun); verb = Util.CanonicalizeName(routeParts.verb); } catch (FormatException) { Log.Warning("Api Operation path {Path} was not of the expected format. Expected /api/[noun]/[verb]", op.Path); return(null, MatchType.Error); } var nameMatch = domain.Operations.Where(dmnOp => dmnOp.RelatedType != null && Util.CanonicalizeName(dmnOp.BareName) == verb && Util.CanonicalizeName(dmnOp.RelatedType.Name) == noun); if (nameMatch.Any()) { if (nameMatch.Count() == 1) { var domainOp = nameMatch.First(); Log.Debug("API operation {Path} matched {OperationName} by name", op.Path, domainOp.Name); if (ParametersMatch(domainOp, domain, op, doc) && ReturnTypesMatch(domainOp, domain, op, doc)) { return(domainOp, MatchType.Exact); } else { return(domainOp, MatchType.Replace); } } else { // TODO - find nearest match? Log.Error("There were multiple operations that matched API operation {OperationName} by name.", op.Path); return(null, MatchType.Error); } } return(null, MatchType.None); }