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); }
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); }
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); }
/// <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); }); }