Coder Social home page Coder Social logo

openapiextensions's Introduction

OpenApiExtensions

OpenApiExtensions Nuget Package is providing out of the box, generating rich Swagger files out of your Aspnet Api code. The Generated Swagger file is enriched by decorating your code with Attributes and comments, and therefore is able to generated a fully autorest compliant Swagger file (and request/response Example files). the generation process is relying on Swashbuckle

Getting Started

Target Project Prerequisites

  • "Aspnet core 3.1 or higher" for your WebApi

How To use

add this code on your startup ConfigureServices() method see usage

            var config = new SwaggerConfig
            {
                PolymorphicSchemaModels = new List<Type> { typeof(WeatherForecast) },
                ModelEnumsAsString = true,
                ReusableParameters = new Dictionary<string, Microsoft.OpenApi.Models.OpenApiParameter>()
                {
                    { "SomeReusableParam", ReusableParameters.GetSomeReusableParameter() },                    
                    { "ApiVersionParameter", ReusableParameters.GetApiVersionParameter() }                    
                },               
                EnableSwaggerSecurityTokenSupport = true                
            };

            services.AddAutorestCompliantSwagger(config);

and on Configure() method

add this at the beginning of the pipeline:

  app.UseAutorestCompliantSwagger(_swaggerConfig);

having these minimal Prerequisites are enough to generate a swagger file.
when running your ArmApi on "localmode" you can now browse to
http://127.0.0.1:<<YOUR PORT>>/swagger/<<YOUR API VERSION>>/swagger.json
and get the file on your browser
or just http://127.0.0.1:<<YOUR PORT>>/swagger to get the swagger ui.

Enrichment

to be able to be fully compliant with Autorest Swagger standards, your code should be decorated with Comments and attributes.

Enrich by Comments:

In order to enrich your swagger file from comments, you need to set msbuild Xml documentation file.
upon build, the complier will read all your triple slash comments and save them into a XML file, which the swagger generation process will automatically read to enrich your Classes, Operations. see example

example:

    /// <summary>
    /// Some Descriptive Summary Description (Reflected on your XML Comment -> and Swashbuckle read the        XmlDocumentation file and enrich the schemas , see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#include-descriptions-from-xml-comments)
    /// </summary>
    abstract public class WeatherForecast
    {

    }

Enrich by Attributes:

As the Swagger Generation is based on Swashbuckle, you may use any of the swashbuckle annotations.
in addition to that you may refer to full list of provided annotation (attributes) by this repo

you might also want to run the code tour of this repo by opening this folder with VSCode, and start the code tour.

ArmApi

    [ApiVersion("2022-01-01-preview")]
    [Route("MyArmApiController")]
    [ApiController]
    [SwaggerApiVersionRange(fromVersion: "2022-01-01-preview")] // this tell the swagger generating to include this controller in any "2022-01-01-preview" and above (not mandatory)
    [SwaggerTag("ReadSomeResource")] // this would set up the tag for your controller
    public class MyArmApiController : ControllerBase
    {
        ....
    }

Internal Api

to avoid Internal Apis to be reflected in External Swagger docs use [HideInDocs] attribute to decorate either your Controllers or Actions

    [ApiVersion("2022-01-01-preview")]
    [Route("InternalApi")]
    [HideInDocs]
    public class MyInternalApiController : ControllerBase
    {
        ....
    }

in this sample we provide the [SwaggerOperation] attribute that enrich the swagger model, and Request+Response + Example Providers.

        [SwaggerOperation(
           Summary = "Get MyArmResources records",
           Description = "Fetches My Arm Resources",
           OperationId = "Get",
           Tags = new[] { "MyArmResources" })]
        [SwaggerResponse(200, "MyArmResources records fetched", typeof(SomeResourceList))]
        [ResponseExample(200, typeof(GetAllMyArmResourcesResponseExample))]
        [RequestExample(typeof(GetAllMyArmResourcesAsyncRequestExample))]
        [Pageable]        
        [HttpGet]
        public async Task<SomeResourceList> GetListOfResources(
            Guid subscriptionId,
            string resourceGroupName,
            string workspaceName,            
            [SwaggerHideParameter][FromHeader(Name = ArmHeaderConstants.InternalWorkspaceIdHeaderName)] Guid workspaceId,
            [LogIgnore] CancellationToken cancellationToken)
        {
          ...
        }
        /// <param name="resourceId">Some Documentation to be shown on swagger</param>
        [SwaggerOperation(
           Summary = "Get MyArmResource record",
           Description = "Fetches My Arm Resource",
           OperationId = "Get",
           Tags = new[] { "MyArmResource" })]
        [SwaggerResponse(200, "MyArmResource fetched", typeof(MyArmResource))]
        [ResponseExample(200, typeof(GetMyArmResourceResponseExample))]
        [RequestExample(typeof(GetAllSomeResourceAsyncRequestExample))]          
        [HttpGet("{resourceId}")]
        public async Task<MyArmResource> GetSingleResource(
            Guid subscriptionId,
            string resourceGroupName,
            string workspaceName,            
            string resourceId,
            [SwaggerHideParameter][FromHeader(Name = ArmHeaderConstants.InternalWorkspaceIdHeaderName)] Guid workspaceId,
            [LogIgnore] CancellationToken cancellationToken)
        {
          ...
        }        

by specifying above your action these attributes : [ResponseExample] and [RequestExample], examples will be auto generated for you. see sample

See [samples](./samples/] projects for more info
for Virtual inheritance in your swagger file refer this sample
(virtual inheritance is when you have polymorphism on your APIs, and the classes in your swagger doesnt exist on your code)

Renaming your WebModels

sometimes you want to call your models differently on the swagger doc than your code.
you have a few options to deal with this.

  1. Renaming your class in code to match swagger, as WebModels class renaming is not a breaking change. you can do so if you can.

  2. If you are not able to control your WebModels, in such cases when returning a generic Object, MyObjectGenericsWrapper<T>, you still can :

    1. Not using MyObjectGenericsWrapper<T> and use your own class.
    2. inherit from MyObjectGenericsWrapper<T> and return this inheritance from your controllers actions.
  3. Use provided Swagger Attributes to control the naming:
    here are some examples:

    • Basic constant renaming:

       // in this example MyBoringClassResource would be named MySwaggerAwesomeName
        [SwaggerSchemaNameStrategy("MySwaggerAwesomeName")]
        public class MyBoringClassResource
        {
        }
    • Naming a generic wrapper:

        [SwaggerSchemaNameStrategy(NamingStrategy.ApplyToParentWrapper, "MySwaggerAwesomeNameModel")]
        public class MyResourceProperties
        {
        }
    
    
        public class MyObjectGenericsWrapper<T> // this class when used as MyObjectGenericsWrapper<MyResourceProperties> will be named in swagger as MySwaggerAwesomeNameModel
        {
        }
    // in this example a Generic Wrapper that holds ConcreteModel would be named according to the logic of SwaggerAwesomeClassNameProvider
    [SwaggerSchemaNameStrategy(NamingStrategy.ApplyToParentWrapper, typeof(SwaggerAwesomeClassNameProvider) )]
    public class ConcreteModel
    {               
    }
    
    public class SwaggerAwesomeClassNameProvider : ICustomSchemaNameProvider
    {
        public string GetCustomName(Type type)
        {
            if (typeof(MyObjectGenericsWrapper<ConcreteModel>) == type)
            {
                return "ConcreteClass";
            }
    
            if (typeof(MyObjectsCollectionGenericsWrapper<ConcreteModel>) == type)
            {
                return "ConcreteClassesList";
            }
    
            throw new InvalidOperationException("Add more here ..");
        }
    }      

Open Api Extension also provide out of the box Custom name providers such as ArmResourceWrapperNameProvider

Models hierarchy

You might need to create a model hierarch in swagger that doesn't reflect your code. to do so, you can use the CustomSwaggerSchemaInheritance to create an object Hierarchy that doesn't reflect your code.
see this example:

    /// <summary>
    /// The Some Resource Properties model.
    /// </summary>    
    [ClientFlatten]    
    [CustomSwaggerSchemaInheritance(externalSchemaName: "MySwaggerOnlyBaseCLass", notInheritedPropertiesName: new[] { nameof(Properties) }, CommonObjectType.ResourceProviderCommonDefinition )]
    public class SomeResourceModel
    {
        /// <summary>
        /// Gets or sets nested level of properties which contains the resource content
        /// </summary>
        [JsonProperty("properties", Required = Required.Always)]
        public MyResourceProperties Properties { get; set; }
    }

will result this Definition in swagger:

 "definitions": {
    "SomeResourceModel": {
      "description": "The Some Resource Properties model.",
      "required": [
        "properties"
      ],
      "type": "object",
      "allOf": [
        {
          "$ref": "../../../common/2.0/types.json#/definitions/MySwaggerOnlyBaseClass"
        }
      ],
      "properties": {
        "properties": {
          "description": "Gets or sets nested level of properties which contains the resource content",
          "type": "object",
          "allOf": [
            {
              "$ref": "#/definitions/MyResourceProperties"
            }
          ],
          "x-ms-client-flatten": true,          
        }
      }
    }
 }

Polymorphic in this context means, that your request or response is/contains a base (or abstract) class.
this can lead to some challenges. in order to overcome it, OpenApiExtension has some out of the box solutions.
for starters you need to register your polymorphic class in the SwaggerConfig:

  • in case of "Virtual Inheritance" - means the inherited classes are not in your code, please refer (to sample)ArmResourceProviderDemo.
  • in other case you need to provide the Discriminator see reference here

Api version is supported out of box by reading the [ApiVersion] attributes on your Controllers.
to support also the "Api Version Fallback" you may Provide the [SwaggerApiVersionRangeAttribute] as well, to specify ranges version of that your api supports.

intro

Since your code now contains everything that is needed to generate a well defined Swagger file, you can leverage an automation script that will:

  1. generate your swagger file
  2. "prettify" it so it would be valid for linters
  3. Generate Autorest csharp SDK so you can observe it.
  4. Validate that the swagger file is compliant

Implement

In order to support automate the process of generating and validating your swagger outcome, you need to set up 2 things:

  1. Setup the script configuration section.
  2. a WebHost function that hosts the app, and the cli tool can call it to run the swagger pipeline. see this example for more info.

openapiextensions's People

Contributors

amirsasson avatar microsoft-github-operations[bot] avatar microsoftopensource avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openapiextensions's Issues

[Action Needed] This repo is inactive

This GitHub repository has been identified as a candidate for archival

This repository has had no activity in more than [x amount of time]. Long periods of inactivity present security and code hygiene risks. Archiving will not prevent users from viewing or forking the code. A banner will appear on the repository alerting users that the repository is archived.

Please see https://aka.ms/sunsetting-faq to learn more about this process.

Action

✍️

❗**If this repository is still actively maintained, please simply close this issue. Closing an issue on a repository is considered activity and the repository will not be archived.🔒

If you take no action, this repository is still inactive 30 days from today it will be automatically archived..

Need more help? 🖐️

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.