public Task <OpenApiDocument> GenerateAsync(string documentName)
        {
            if (documentName == null)
            {
                throw new ArgumentNullException(nameof(documentName));
            }

            foreach (var group in _documents.GroupBy(g => g.DocumentName))
            {
                if (group.Count() > 1)
                {
                    throw new ArgumentException("The OpenAPI/Swagger document '" + group.Key + "' registered multiple times: " +
                                                "Explicitly set the DocumentName property in " +
                                                nameof(NSwagServiceCollectionExtensions.AddSwaggerDocument) + "() or " +
                                                nameof(NSwagServiceCollectionExtensions.AddOpenApiDocument) + "().");
                }
            }

            var document = _documents.SingleOrDefault(g => g.DocumentName == documentName);

            if (document?.Settings == null)
            {
                throw new InvalidOperationException($"No registered OpenAPI/Swagger document found for the document name '{documentName}'. " +
                                                    $"Add with the AddSwagger()/AddOpenApi() methods in ConfigureServices().");
            }

            var generator = new AspNetCoreOpenApiDocumentGenerator(document?.Settings);

            return(generator.GenerateAsync(_serviceProvider));
        }
        public async Task QueryParametersAreDiscovered()
        {
            //// Arrange
            var generator = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings {
                RequireParametersWithoutDefault = true
            });
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithParameters));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation         = Assert.Single(document.Operations, o => o.Path == "/" + nameof(ControllerWithParameters.QueryParameter)).Operation;
            var requiredParameter = operation.Parameters.First();
            var optionalParameter = operation.Parameters.Last();

            Assert.Equal(OpenApiParameterKind.Query, requiredParameter.Kind);
            Assert.Equal(OpenApiParameterKind.Query, optionalParameter.Kind);

            Assert.Equal("queryParameter1", requiredParameter.Name);
            Assert.Equal("queryParameter2", optionalParameter.Name);

            Assert.True(requiredParameter.IsRequired);
            Assert.False(optionalParameter.IsRequired);
        }
        /// <summary>Occurs after the action method is invoked.</summary>
        /// <param name="context">The action executed context.</param>
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception != null && (_exceptionTypes.Count == 0 || _exceptionTypes.Exists(t => t.IsInstanceOfType(context.Exception))))
            {
                var settings = AspNetCoreOpenApiDocumentGenerator.GetJsonSerializerSettings(context.HttpContext?.RequestServices);
                settings = settings != null?CopySettings(settings) : new JsonSerializerSettings();

                settings.Converters.Add(new JsonExceptionConverter(_hideStackTrace, _searchedNamespaces));

                var json = JsonConvert.SerializeObject(context.Exception, settings);
                context.Result = new ContentResult
                {
                    StatusCode  = GetStatusCode(context.Exception, context),
                    Content     = json,
                    ContentType = "application/json"
                };

                // Required otherwise the framework exception handlers ignores the
                // Result and redirects to a error page or displays in dev mode the stack trace.
                context.ExceptionHandled = true;
            }
            else
            {
                base.OnActionExecuted(context);
            }
        }
        public async Task SwaggerOperationMethods_AreParsed()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(HttpMethodsController));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            Assert.Collection(
                document.Operations.OrderBy(o => o.Method.ToString()),
                operation =>
            {
                Assert.Equal(OpenApiOperationMethod.Delete, operation.Method);
                Assert.Equal("HttpMethods_HttpDelete", operation.Operation.OperationId);
            },
                operation =>
            {
                Assert.Equal(OpenApiOperationMethod.Head, operation.Method);
                Assert.Equal("HttpMethods_HttpHead", operation.Operation.OperationId);
            },
                operation =>
            {
                Assert.Equal(OpenApiOperationMethod.Options, operation.Method);
                Assert.Equal("HttpMethods_HttpOptions", operation.Operation.OperationId);
            },
                operation =>
            {
                Assert.Equal(OpenApiOperationMethod.Patch, operation.Method);
                Assert.Equal("HttpMethods_HttpPatch", operation.Operation.OperationId);
            });
        }
        public async Task When_generating_swagger_all_apidescriptions_are_discovered()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(TestController));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operationDescription = Assert.Single(document.Operations);

            Assert.Equal("/test", operationDescription.Path);
            Assert.Equal(OpenApiOperationMethod.Get, operationDescription.Method);

            var operation = operationDescription.Operation;

            Assert.Equal("Test_FindModel", operation.OperationId);

            var parameter = Assert.Single(operation.Parameters);

            Assert.Equal("id", parameter.Name);
            Assert.Equal(OpenApiParameterKind.Path, parameter.Kind);
            Assert.True(parameter.IsRequired);
            Assert.Equal(NJsonSchema.JsonObjectType.Integer, parameter.Type);

            Assert.Single(operation.Responses);
            var response   = operation.Responses["200"];
            var definition = document.Definitions.First(f => f.Value == response.Schema?.ActualSchema);

            Assert.Equal(nameof(TestModel), definition.Key);
        }
        public async Task ComplexQueryParametersAreProcessed()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithParameters));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations, o => o.Path == "/" + nameof(ControllerWithParameters.ComplexFromQueryParameter)).Operation;

            Assert.Collection(
                operation.Parameters.OrderBy(p => p.Name),
                parameter =>
            {
                Assert.Equal(nameof(ComplexModel.Header), parameter.Name);
                Assert.Equal(OpenApiParameterKind.Query, parameter.Kind);
            },
                parameter =>
            {
                Assert.Equal(nameof(ComplexModel.Id), parameter.Name);
                Assert.Equal(OpenApiParameterKind.Query, parameter.Kind);
            },
                parameter =>
            {
                Assert.Equal(nameof(ComplexModel.QueryValues), parameter.Name);
                Assert.Equal(OpenApiParameterKind.Query, parameter.Kind);
            });
        }
        public async Task BoundPropertiesAreProcessed()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithBoundProperties));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations).Operation;

            Assert.Collection(
                operation.Parameters.OrderBy(p => p.Name),
                parameter =>
            {
                Assert.Equal(nameof(ControllerWithBoundProperties.HeaderValue), parameter.Name);
                Assert.Equal(OpenApiParameterKind.Header, parameter.Kind);
            },
                parameter =>
            {
                Assert.Equal(nameof(ControllerWithBoundProperties.Id), parameter.Name);
                Assert.Equal(OpenApiParameterKind.Path, parameter.Kind);
            },
                parameter =>
            {
                Assert.Equal("model", parameter.Name);
                Assert.Equal(OpenApiParameterKind.Body, parameter.Kind);
            });
        }
        public async Task SwaggerOperationProcessorAttributesOnActions_AreDiscoveredAndExecuted()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ActionWithSwaggerOperationProcessor));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            Assert.Equal("Hello from action", document.Info.Title);
        }
        public async Task ControllersWithSwaggerIgnoreAttribute_AreIgnored()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithSwaggerIgnoreAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            Assert.Empty(document.Operations);
        }
Exemple #10
0
        protected async Task <OpenApiDocument> GenerateDocumentAsync(AspNetCoreOpenApiDocumentGeneratorSettings settings, params Type[] controllerTypes)
        {
            var generator = new AspNetCoreOpenApiDocumentGenerator(settings);
            var provider  = TestServer.Host.Services.GetRequiredService <IApiDescriptionGroupCollectionProvider>();

            var controllerTypeNames = controllerTypes.Select(t => t.FullName);
            var groups = new ApiDescriptionGroupCollection(provider.ApiDescriptionGroups.Items
                                                           .Select(i => new ApiDescriptionGroup(i.GroupName, i.Items.Where(u => controllerTypeNames.Contains(((ControllerActionDescriptor)u.ActionDescriptor).ControllerTypeInfo.FullName)).ToList())).ToList(),
                                                           provider.ApiDescriptionGroups.Version);

            var document = await generator.GenerateAsync(groups);

            return(document);
        }
        public async Task SwaggerOperationAttribute_AreUsedToCalculateOperationId_IfPresent()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ActionWithSwaggerOperationAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations);

            Assert.Equal("CustomOperationId", operation.Operation.OperationId);
        }
        public async Task ActionsWithSwaggerIgnoreAttribute_AreIgnored()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ActionWithSwaggerIgnoreAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operationDescription = Assert.Single(document.Operations);

            Assert.Equal("/test1", operationDescription.Path);
            Assert.Equal(OpenApiOperationMethod.Get, operationDescription.Method);
        }
        public async Task FormFileParametersAreDiscovered()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithParameters));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations, o => o.Path == "/" + nameof(ControllerWithParameters.FileParameter)).Operation;
            var parameter = Assert.Single(operation.Parameters);

            Assert.Equal(OpenApiParameterKind.FormData, parameter.Kind);
            Assert.Equal("multipart/form-data", Assert.Single(operation.Consumes));
        }
        public async Task FromHeaderParametersAreDiscovered()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithParameters));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations, o => o.Path == "/" + nameof(ControllerWithParameters.FromHeaderParameter)).Operation;
            var parameter = Assert.Single(operation.Parameters);

            Assert.Equal(OpenApiParameterKind.Header, parameter.Kind);
            Assert.Equal("headerParameter", parameter.Name);
        }
        public async Task When_no_IncludedVersions_are_defined_then_all_routes_are_available_and_replaced()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithReCodeAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations).Operation;

            Assert.True(operation.ExtensionData.ContainsKey("x-code-samples"));
            var extenstionData = (IList <ReDocCodeSampleAttribute.ReDocCodeSample>)operation.ExtensionData["x-code-samples"];

            Assert.Equal(2, extenstionData.Count);
        }
        public async Task SwaggerResponseAttributesOnActionsAreDiscovered()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ActionWithSwaggerResponseAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations);

            Assert.Single(operation.Operation.Responses);
            var response   = operation.Operation.Responses["201"];
            var definition = document.Definitions.First(f => f.Value == response.Schema?.ActualSchema);

            Assert.Equal(nameof(TestModel), definition.Key);
        }
        public async Task FromFormParametersAreDiscovered()
        {
            //// Arrange
            var generator = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings {
                RequireParametersWithoutDefault = true
            });
            var apiDescriptions = GetApiDescriptionGroups(typeof(ControllerWithParameters));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operation = Assert.Single(document.Operations, o => o.Path == "/" + nameof(ControllerWithParameters.FromFormParameter)).Operation;
            var parameter = Assert.Single(operation.Parameters);

            Assert.Equal(OpenApiParameterKind.FormData, parameter.Kind);
            Assert.Equal("formParameter", parameter.Name);
            Assert.True(parameter.IsRequired);
        }
Exemple #18
0
        public async Task WhenSystemTextOptionsIsUsed_ThenOptionsAreConverted()
        {
            // Arrange
            var services = new ServiceCollection()
                           .AddLogging();

            services.AddControllers()
            .AddJsonOptions(opt => opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

            services.AddOpenApiDocument();
            var serviceProvider = services.BuildServiceProvider();

            // Act
            var registration = serviceProvider.GetRequiredService <OpenApiDocumentRegistration>();
            var generator    = new AspNetCoreOpenApiDocumentGenerator(registration.Settings);
            await generator.GenerateAsync(serviceProvider);

            // Assert
            Assert.Contains(registration.Settings.SerializerSettings.Converters, c => c is StringEnumConverter);
        }
        public async Task ParametersWithSwaggerIgnoreAttribute_AreIgnored()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = GetApiDescriptionGroups(typeof(ParameterWithSwaggerIgnoreAttribute));

            //// Act
            var document = await generator.GenerateAsync(apiDescriptions);

            //// Assert
            var operationDescription = Assert.Single(document.Operations);

            Assert.Equal("/{id}", operationDescription.Path);
            Assert.Equal(OpenApiOperationMethod.Post, operationDescription.Method);

            var parameter = Assert.Single(operationDescription.Operation.Parameters);

            Assert.Equal("id", parameter.Name);
            Assert.Equal(OpenApiParameterKind.Path, parameter.Kind);
        }
        /// <summary>Adds services required for Swagger 2.0 generation (change document settings to generate OpenAPI 3.0).</summary>
        /// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
        /// <param name="configure">Configure the document.</param>
        public static IServiceCollection AddSwaggerDocument(this IServiceCollection serviceCollection, Action <AspNetCoreOpenApiDocumentGeneratorSettings, IServiceProvider> configure = null)
        {
            serviceCollection.AddSingleton(services =>
            {
                var settings = new AspNetCoreOpenApiDocumentGeneratorSettings
                {
                    SchemaType = SchemaType.Swagger2,
                };

                configure?.Invoke(settings, services);

                foreach (var documentProcessor in services.GetRequiredService <IEnumerable <IDocumentProcessor> >())
                {
                    settings.DocumentProcessors.Add(documentProcessor);
                }

                foreach (var operationProcessor in services.GetRequiredService <IEnumerable <IOperationProcessor> >())
                {
                    settings.OperationProcessors.Add(operationProcessor);
                }

                var generator = new AspNetCoreOpenApiDocumentGenerator(settings);
                return(new OpenApiDocumentRegistration(settings.DocumentName, generator));
            });

            var descriptor = serviceCollection.SingleOrDefault(d => d.ServiceType == typeof(OpenApiDocumentProvider));

            if (descriptor == null)
            {
                serviceCollection.AddSingleton <OpenApiDocumentProvider>();
                serviceCollection.AddSingleton <IConfigureOptions <MvcOptions>, OpenApiConfigureMvcOptions>();

                // Used by UseDocumentProvider CLI setting
                serviceCollection.AddSingleton <IOpenApiDocumentGenerator>(s => s.GetRequiredService <OpenApiDocumentProvider>());

                // Used by the <c>dotnet-getdocument</c> tool from the Microsoft.Extensions.ApiDescription.Server package.
                serviceCollection.AddSingleton <IDocumentProvider>(s => s.GetRequiredService <OpenApiDocumentProvider>());
            }

            return(serviceCollection);
        }
Exemple #21
0
 /// <summary>Initializes a new instance of the <see cref="OpenApiDocumentRegistration"/> class.</summary>
 /// <param name="documentName">The document name.</param>
 /// <param name="generator">The document generator.</param>
 public OpenApiDocumentRegistration(string documentName, AspNetCoreOpenApiDocumentGenerator generator)
 {
     DocumentName = documentName;
     Generator    = generator;
 }
        public async Task SwaggerDocumentIsGeneratedForCustomCreatedApiDescriptions()
        {
            //// Arrange
            var generator       = new AspNetCoreOpenApiDocumentGenerator(new AspNetCoreOpenApiDocumentGeneratorSettings());
            var apiDescriptions = new[]
            {
                new ApiDescription
                {
                    ActionDescriptor = new ActionDescriptor(),
                },
                new ApiDescription
                {
                    ActionDescriptor = new ControllerActionDescriptor
                    {
                        ControllerTypeInfo = typeof(CustomController).GetTypeInfo(),
                        MethodInfo         = typeof(CustomController).GetMethod(nameof(CustomController.FindModel)),
                        AttributeRouteInfo = new AttributeRouteInfo
                        {
                            Template = "api/test/{id}",
                        },
                        ControllerName = "Test",
                        ActionName     = "Find",
                    },
                    HttpMethod            = "Get",
                    RelativePath          = "api/test/{id}",
                    ParameterDescriptions =
                    {
                        new ApiParameterDescription
                        {
                            Name   = "id",
                            Source = BindingSource.Path,
                            Type   = typeof(int),
                        },
                    },
                    SupportedResponseTypes =
                    {
                        new ApiResponseType
                        {
                            Type       = typeof(TestModel),
                            StatusCode = 200,
                        }
                    },
                },
                new ApiDescription
                {
                    ActionDescriptor = new ControllerActionDescriptor
                    {
                        ControllerTypeInfo = typeof(CustomController).GetTypeInfo(),
                        MethodInfo         = typeof(CustomController).GetMethod(nameof(CustomController.Delete)),
                        ControllerName     = "Test",
                        ActionName         = "DeleteModel",
                    },
                    HttpMethod            = "Delete",
                    RelativePath          = "api/test",
                    ParameterDescriptions =
                    {
                        new ApiParameterDescription
                        {
                            Name   = "id",
                            Source = BindingSource.Query,
                            Type   = typeof(int),
                        },
                    },
                    SupportedResponseTypes =
                    {
                        new ApiResponseType
                        {
                            Type       = typeof(StatusCodeResult),
                            StatusCode = 201,
                        }
                    },
                },
                new ApiDescription
                {
                    ActionDescriptor = new ControllerActionDescriptor
                    {
                        ControllerTypeInfo = typeof(CustomController).GetTypeInfo(),
                        MethodInfo         = typeof(CustomController).GetMethod(nameof(CustomController.Update)),
                        AttributeRouteInfo = new AttributeRouteInfo
                        {
                            Template = "api/test/{id}",
                        },
                        ControllerName = "Test",
                        ActionName     = "Update",
                    },
                    HttpMethod            = "Put",
                    RelativePath          = "api/test/{id}",
                    ParameterDescriptions =
                    {
                        new ApiParameterDescription
                        {
                            Type   = typeof(int),
                            Name   = "id",
                            Source = BindingSource.Path,
                        },
                        new ApiParameterDescription
                        {
                            Type   = typeof(TestModel),
                            Name   = "model",
                            Source = BindingSource.Body,
                        },
                    },
                    SupportedResponseTypes =
                    {
                        new ApiResponseType
                        {
                            Type       = typeof(Task <TestModel>),
                            StatusCode = 200,
                        }
                    },
                },
            };
            var apiDescriptionGroupCollection = new ApiDescriptionGroupCollection(
                new[] { new ApiDescriptionGroup(string.Empty, apiDescriptions) },
                version: 1);

            //// Act
            var document = await generator.GenerateAsync(apiDescriptionGroupCollection);

            //// Assert
            Assert.Collection(
                document.Operations.OrderBy(o => o.Method.ToString()),
                operation =>
            {
                Assert.Equal("/api/test", operation.Path);
                Assert.Equal(OpenApiOperationMethod.Delete, operation.Method);
            },
                operation =>
            {
                Assert.Equal("/api/test/{id}", operation.Path);
                Assert.Equal(OpenApiOperationMethod.Get, operation.Method);

                Assert.Single(operation.Operation.Responses);
                var response   = operation.Operation.Responses["200"];
                var definition = document.Definitions.First(f => f.Value == response.Schema?.ActualSchema);
                Assert.Equal(nameof(TestModel), definition.Key);
            },
                operation =>
            {
                Assert.Equal("/api/test/{id}", operation.Path);
                Assert.Equal(OpenApiOperationMethod.Put, operation.Method);
            });
        }