buildingthingswith.net

Comparison of code-generation tools

OpenAPI Generator

OpenAPI Usage

Given, some valid OpenAPI spec the following can be done.

generate server stub from open-api spec

docker run --rm -v %CD%\local:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g aspnetcore -o /local/out/aspnetcore

output mustache templates

docker run --rm -v %CD%\local:/local openapitools/openapi-generator-cli author template -g aspnetcore -o /local/template/aspnetcore

view list of mustache templates here:

  1. all templates
  2. aspnetcore

create custom templates

docker run --rm -v %CD%\local:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g aspnetcore -o /local/out/aspnetcore -c /local/config.yaml

create custom generator

docker run --rm -v %CD%\local:/local openapitools/openapi-generator-cli meta -o /local/generators/my-aspnetcore -n my-aspnetcore -p com.my.aspnetcore

customize open-api template. below is an example of the controller.mustache template


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;

using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;


using Newtonsoft.Json;

using .Attributes;
using ;

namespace 
{ 
    /// <summary>
    /// 
    /// </summary>
    [Description("")]
    [ApiController]
    public  class Controller : ControllerBase
    { 
        /// <summary>
        /// 
        /// </summary>
        /// <remarks></remarks>
        /// <param name=""> (deprecated)</param>
        /// <response code=""></response>
        []
        [Route("}}")]


        [Authorize(Policy = "")]


        [Authorize(Roles = ",")]


        
        [Consumes()]
        
        [ValidateModelState]
        [SwaggerOperation("")]
        [SwaggerResponse(statusCode: , type: typeof(), description: "")]
        [ProducesResponseType(statusCode: , type: typeof())]
        
        [Obsolete]
        
        public  async Task<IActionResult> (, );
        
        {
            
            var  = Request.Cookies[""];
            



            //TODO: Uncomment the next line to return response  or use other options such as return this.NotFound(), return this.BadRequest(..), ...
            // return StatusCode(, default());


            //TODO: Uncomment the next line to return response  or use other options such as return this.NotFound(), return this.BadRequest(..), ...
            // return StatusCode();



            string exampleJson = null;
            
            exampleJson = "}";
            
            
            
            //TODO: Change the data returned
            return Task.FromResult<IActionResult>(new ObjectResult(example));
            throw new NotImplementedException();
        }
        
        
    }

}

OpenAPI Findings

Pros:

Cons:

Yeoman generator

Yeoman Usage

  1. install node with chocolatey
  2. install yo:
npm install -g yo
yo --version
# install a generator
npm install -g <generator-name>

# e.g. 
npm install -g generator-webapp

# scaffold new project using installed generator
yo <generator-name>

# e.g. 
yo webapp

# use sub-generators, e.g. add a controller
yo <generator-name>:<sub-generator-name>

# troubleshoot & debug
yo doctor
yo --generators
yo --help
yo --version
# create folder to hold generator, name is NB: generator-name, e.g. generator-sample
cd generator-name

# create package.json in folder
echo '' > package.json or npm init
npm install --save yeoman-generator

# create folders
# ├───package.json
# └───generators/
#     ├───app/
#     │   └───index.js
#     └───router/
#         └───index.js

npm link

yo <generator-name> # e.g. yo sample [doesn't need 'generator' prefixing it]

The available priorities are (in running order):

initializing - Your initialization methods (checking current project state, getting configs, etc) prompting - Where you prompt users for options (where you’d call this.prompt()) configuring - Saving configurations and configure the project (creating .editorconfig files and other metadata files) default - If the method name doesn’t match a priority, it will be pushed to this group. writing - Where you write the generator specific files (routes, controllers, etc) conflicts - Where conflicts are handled (used internally) install - Where installations are run (npm, bower) end - Called last, cleanup, say good bye, etc

Yeoman Sample

namespace <%= namespace %>.Controllers
{
    [Route("api")]
    [ApiController]
    public class AccountController : ControllerBase
    {

        <%_ if (authenticationType === 'jwt') { _%>
        private readonly ILogger<AccountController> _log;
        <%_ if (cqrsEnabled) { _%>
        private readonly IMediator _mediator;
        <%_ } else { _%>
        private readonly IMapper _userMapper;
        private readonly IMailService _mailService;
        private readonly UserManager<User> _userManager;
        private readonly IUserService _userService;
        <%_ } _%>

        <%_ if (cqrsEnabled) { _%>
        public AccountController(ILogger<AccountController> log, IMediator mediator)
        {
            _log = log;
            _mediator = mediator;
        }
        <%_ } else { _%>
        public AccountController(ILogger<AccountController> log, UserManager<User> userManager, IUserService userService,
            IMapper userMapper, IMailService mailService)
        {
            _log = log;
            _userMapper = userMapper;
            _userManager = userManager;
            _userService = userService;
            _mailService = mailService;
        }
        <%_ } _%>

        [HttpPost("register")]
        [ValidateModel]
        public async Task<IActionResult> RegisterAccount([FromBody] ManagedUserDto managedUserDto)
        {
            if (!CheckPasswordLength(managedUserDto.Password)) throw new InvalidPasswordException();
            <%_ if (cqrsEnabled) { _%>
            var user = await _mediator.Send(new AccountCreateCommand { ManagedUserDto = managedUserDto });
            <%_ } else { _%>
            var user = await _userService.RegisterUser(_userMapper.Map<User>(managedUserDto), managedUserDto.Password);
            await _mailService.SendActivationEmail(user);
            <%_ } _%>
            return CreatedAtAction(nameof(GetAccount), user);
        }

        /// etc...
        }
    }    
}    

Yeoman Findings

Pros:

Cons:

T4 templates

T4 Usage

T4 Findings

Roslyn API

Roslyn Usage

Roslyn Findings

OpenAPI Docs

Yeoman Docs

T4 Docs

Roslyn Docs

Other References

Home