Skip to content

staycaffeinated/metacode

Repository files navigation

MetaCode Code Generator

Features

  • Generates a working application of these types:

    • Spring WebMvc

    • Spring WebFlux

      • For WebFlux and WebMvc, a project with a single controller will have about 600 lines of code and about 80 tests generated

    • Spring Boot

    • Spring Batch

  • Generates unit and integration tests that currently provide 100% code coverage

  • Assigns resource objects unique identifiers that have 160-bit entropy, the highest amount recommended by OAuth2 (160 bits is approximately 1.46 x 1048 possible values).

  • Supported Integrations:

    • Postges

    • TestContainers

    • Liquibase

With Postgres integration, the JPA properties within application.properties reference Postgres JDBC drivers. With TestContainers integration, the test cases use TestContainers to emulate the database. When both Postgres and TestContainers are used, the tests use the Postgres flavor of the test container. With Liquibase, a simple configuration is generated as a starting point.

Homebrew Install

Metacode is available on Homebrew. To install it, run these commands:

$ brew tap staycaffeinated/tap
$ brew install metacode

The Metacode CLI requires Java 17 to run. The generated code supports Java 11 and higher.

General Usage

To get a general idea of the metacode command usage, type:

metacode --help

You will always start with the create project subcommand, which lays down the base code and Gradle artifacts of the project. The create project subcommand writes files in your current directory, so always be sure to create your new project’s working directory, navigate into that directory, then run the create project subcommand. For example:

$ mkdir customer-importer
$ cd customer-importer
$ metacode create project spring-batch -n customer-importer -p acme.customer.batch --base-path /customer-importer
$ gradle wrapper
$ ./gradlew build

Quick Start Guide

To demonstrate how to generate a project with MetaCode, we’ll walk through generating Spring’s Pet Store example, with three endpoints: pets, owners, and stores.

The Spring Pet Store in Spring WebFlux

We’ll start with Spring WebFlux, which uses asynchronous communication, with Mono and Flux streams.

Be aware that MetaCode creates everything in the current working directory, so create the project directory first, navigate into it, then run the metacode command.
Step 1: Create the basic project.
$ mkdir petstore-webflux
$ cd petstore-webflux
$ metacode create project spring-webflux --name petstore --package acme.petstore --base-path /petstore
Step 2: Create an endpoint for pets
$ metacode create endpoint --resource Pet --route /pets
Step 3: Create a Gradle wrapper.
$ gradle wrapper
Step 4: Compile the code
$ ./gradlew build
Step 5: Create endpoints for stores and owners.
$ metacode create endpoint --resource Store --route /stores
$ metacode create endpoint --resource Owner --route /owners
$ ./gradlew build
Step 6: Run the application again to see where things stand
$ ./gradlew bootRun

Try these URLs in your browser to see what happens

$ http localhost:8080/petstore/pets/findAll
$ http localhost:8080/petstore/stores/findAll
$ http localhost:8080/petstore/owners/findAll
$ http localhost:8080/petstore/pets
$ http localhost:8080/petstore/pets/[resourceId]
$ http localhost:8080/petstore/_internal/health
$ http localhost:8080/petstore/_internal/health/liveness

# Try a resource ID that does not exist
$ http localhost:8080/petstore/pets/55555

# Attempt a JavaScript injection
$ http http "localhost:8080/petstore/pets/'<alert>hello</alert>'"

The Spring Pet Store in Spring WebMvc

This example shows using Spring WebMvc, which uses synchronous communication.

Step 1: Create the basic project.
$ mkdir petstore-webmvc
$ cd petstore-webmvc
$ metacode create project spring-webmvc --name petstore --package acme.petstore --base-path /petstore
Step 2: Create an endpoint for pets
$ metacode create endpoint --resource Pet --route /pets
Step 3: Create a Gradle wrapper.
$ gradle wrapper
Step 4: Compile the code
$ ./gradlew build
Step 5: Create endpoints for stores and owners.
$ metacode create endpoint --resource Store --route /stores
$ metacode create endpoint --resource Owner --route /owners
$ ./gradlew build
Step 6: Run the application again to see where things stand
$ ./gradlew bootRun

Try these URLs in your browser to see what happens

$ http localhost:8080/petstore/pets/findAll
$ http localhost:8080/petstore/stores/findAll
$ http localhost:8080/petstore/owners/findAll
$ http localhost:8080/petstore/pets
$ http localhost:8080/petstore/pets/[resourceId]
$ http localhost:8080/petstore/_internal/health
$ http localhost:8080/petstore/_internal/health/liveness

# Because the question mark has a special meaning in bash and zsh,
# use quotes around the URL string, as done here.
# (See: https://scriptingosx.com/2017/08/special-characters/ )
$ http "localhost:8080/petstore/search?text=Five"

Generate a Spring Boot project

When you only want the bare minimum of code generated, the spring-boot template is available. The spring-boot template generates an Application.java, some helper classes, and an ApplicationTests.java.

To create such a project, following this recipe:

$ mkdir basic-springboot
$ cd basic-springboot
$ metacode create project spring-boot --name hello-world --package acme.greeting --base-path /greeting

Generate a Spring Batch project

The spring-batch subcommand creates a simple Spring Batch application. Mainly, the Application.java class, an example JobCompletionNotificationListener.java and a very basic BatchConfiguration.java class. From there, the developer has to build out the rest of the application. The BatchConfiguration class does include comments with suggested next steps for those new to Spring Batch applications.

To create such a Spring Batch project, following this recipe:

$ mkdir basic-springbatch
$ cd basic-springbatch
$ metacode create project spring-batch --name batch-example --package acme.example.batch --base-path /batch-example

Caveats

If compile errors are encounted after generating endpoint assets, the most likely cause is name collisions. For example, if you do this:

$ metacode create endpoint --resource Controller --route /controller

You will get compile errors because the compile will encounter your Controller class and the org.springframework.stereotype.Controller class. Likewise, if you do

$ metacode create endpoint --resource Test --route /test

You will get compile errors because the compiler will encounter your Test class and the org.junit.jupiter.api.Test class.

The code generator could address this by using fully-qualified class names for all generated classes, but that feels to onerous; the generated code would have this style:

    acme.petstore.database.pet.Pet getPet ( String petId );

and

    void savePet (acme.petstore.database.pet.Pet pet) { ... }

which works, but becomes tedious to read. The more reasonable solution is to be aware of this behavior and be prepared to open your IDE to resolve these.