Generating client code with NSwag for Enumeration class

· Architecture and Design, ASP.NET Core, asp .net core 3.1, Domain Driven Design, Enum, Enumeration class, Microservices, .NET Core, NSwag, NSwagStudio, swagger

This is my fourth post in the Series: Enumeration classes – DDD and beyond. If you are new to the Enumeration class, I suggest going through my previous posts.

NuGet and source code

The Enumeration class and other dependent classes are available as the NuGet packages. You can find the source code for the series at this GitHub link.

What is NSwag?

From the NSwag GitHub documentation:

NSwag is a Swagger/OpenAPI 2.0 and 3.0 toolchain for .NET, .NET Core, Web API, ASP.NET Core, TypeScript (jQuery, AngularJS, Angular 2+, Aurelia, KnockoutJS and more) and other platforms, written in C#. The OpenAPI/Swagger specification uses JSON and JSON Schema to describe a RESTful web API. The NSwag project provides tools to generate OpenAPI specifications from existing ASP.NET Web API controllers and client code from these OpenAPI specifications.

If you are new to NSwag, Microsoft documentation here will come handy. Also, please have a look at youtube video created by my former colleague and friend,Rahul, where he has described how to use NSwag and NSwagStudio in detail.

The sample API

Let us go back to our old PaymentTypeEnumeration class.

public abstract class PaymentType : Enumeration
{
    public static readonly PaymentType DebitCard = new DebitCardType();

    public static readonly PaymentType CreditCard = new CreditCardType();

    public abstract string Code { get; }

    private PaymentType(int value, string name = null) : base(value, name)
    {
    }

    private class DebitCardType : PaymentType
    {
        public DebitCardType() : base(0, "DebitCard")
        {
        }

        public override string Code => "DC";
    }

    private class CreditCardType : PaymentType
    {
        public CreditCardType() : base(1, "CreditCard")
        {
        }

        public override string Code => "CC";
    }
}

Consider a simple HttpPostendpoint, which accepts a Transactionobject in the body.

public class Transaction
{
    public PaymentType PaymentType { get; set; }

    public decimal Amount { get; set; }
}
[ApiController]
[Route("[controller]")]
public class TransactionController : ControllerBase
{
    private static readonly List<Transaction> s_Transactions = new List<Transaction>();

    [HttpPost]
    [Route("create")]
    public IActionResult CreateTransaction(Transaction transaction)
    {
        s_Transactions.Add(transaction);
        return Ok();
    }
}

As you can see in the above code, TransactionDTO has a property PaymentType..

NSwag does not support System.Text.Jsonat the time of this writing. As a result, we need to fallback to our trustworthy old friend NewtonsoftJson. We will configure the API to use NewtonsoftJson and add **EnumerationJsonConverter,**as explained in Part 2 of this series.

// Import Microsoft.AspNetCore.Mvc.NewtonsoftJson

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.Converters.Add(new EnumerationJsonConverter());
    });
}

Testing Api on Postman

The Postman request accepts the PaymentType as both as name and value, just like a normal EnumType.

Figure 1: Using PaymentType as name in request body

Figure 1: Using PaymentType as name in request body

Figure 2: Using PaymentType as value in request body

Figure 2: Using PaymentType as value in request body

Adding Swagger to Api

To generate client code from NSwag, let us first add Swagger support to our API.

// Import Swashbuckle.AspNetCore.SwaggerGen
// Import Swashbuckle.AspNetCore.SwaggerUI

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.Converters.Add(new EnumerationJsonConverter());
    });

    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new OpenApiInfo {Title = "Enumeration NSwagger Sample", Version = "v1"});
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();

        app.UseSwagger();

        app.UseSwaggerUI(options =>
        {
            options.SwaggerEndpoint("v1/swagger.json", "Enumeration NSwagger Sample");
        });
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

The swagger page of our application would look similar to below.

Figure 3: PaymentType rendered as a complex object on Swagger

Figure 3: PaymentType rendered as a complex object on Swagger

Notice that the PaymentTypeschema is as a complex object instead of an Enum type.

To fix this, we need to extend our OpenAPI specification by creating a new SchemaFilter that renders an Enumeration class as an Enum type in Swagger.

// Import Swashbuckle.AspNetCore.SwaggerGen

public class EnumerationToEnumSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (!context.Type.IsSubclassOf(typeof(Enumeration)))
        {
            return;
        }

        var fields = context.Type.GetFields(BindingFlags.Static | BindingFlags.Public);

        schema.Enum = fields.Select(field => new OpenApiString(field.Name)).Cast<IOpenApiAny>().ToList();
        schema.Type = "string";
        schema.Properties = null;
        schema.AllOf = null;
    }
}

Next, we need to add EnumerationToEnumSchemaFilterto the SwaggerGenoptions in Startup.cs.

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo {Title = "Enumeration NSwagger Sample", Version = "v1"});

    options.SchemaFilter<EnumerationToEnumSchemaFilter>();
});

If we run our application now, the PaymentType schema is an Enum type.

Figure 4: PaymentType rendered as a Enum type on Swagger

Figure 4: PaymentType rendered as a Enum type on Swagger

Generating client code from NSwagger Studio

Now, we let us generate the client using NSwagger studio. We need to specify the path to swagger.json and click Generate Outputs. Note the schema for PaymentTypein OpenAPI specification. It is of an Enum type just like on the Swagger page.

Figure 5: Open Api/ Swagger specification on NSwagStudio

Figure 5: Open Api/ Swagger specification on NSwagStudio

The generated C# client also creates an Enum type.

Figure 6:  Generated C# client code through NSwagStudio

Figure 6: Generated C# client code through NSwagStudio

Wrapping up

This post explains how we can render an Enumeration class as an Enum type in our client code generated through NSwag. I hope this series has given a complete picture of how we can replace Enumeration class at various parts of our system, including but not limited to web Api, persistence, and Domain-Driven-Design.

#Architecture and Design #ASP.NET Core #C# #Domain Driven Design #Enumeration #.NET #.NET Core #NSwag #NSwagStudio #SchemaFilter #swagger