MicroGen is a tool to generate java code based on an OpenAPI Spec file.
- Server API Contract (JAX-RS)
- Request and Response java objects
- Inner static
Builder
class and utility methods
- Inner static
- Enum value classes
@BeanParams
for request parameters- Maven Archetype to quick start a JakartaEE + MicroProfile application
- Dockerfile(s) with a JakartaEE + MicroProfile application server
Given an OpenAPI Spec file:
openapi: 3.0.0
info:
description: This is a sample server Petstore server.
version: 1.0.0
title: Swagger Petstore
tags:
- name: pet
description: Everything about your Pets
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
components:
requestBodies:
Pet:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
description: Pet object that needs to be added to the store
required: true
schemas:
Pet:
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
items:
type: string
tags:
type: array
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
MicroGen will generate the following:
JAX-RS interface with all the annotations necessary annotations
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.MicroGen")
public interface PetApi {
/**
* Add a new pet to the store
*
* @param pet Pet object that needs to be added to the store (required)
* @return {@code java.util.concurrent.CompletionStage<javax.ws.rs.core.Response>}
*/
@javax.ws.rs.POST
@javax.ws.rs.Path("pet")
@javax.ws.rs.Consumes({"application/json", "application/xml"})
java.util.concurrent.CompletionStage<javax.ws.rs.core.Response> addPet(
@javax.validation.constraints.NotNull @javax.validation.Valid Pet pet
) throws javax.ws.rs.WebApplicationException;
.
.
.
}
@BeanParams
wrapper class to minimize the breaking changes in the Java API contract, this includes:
@PathParam
's@QueryParam
's@FormParam
's@HeaderParam
's@CookieParam
's
But also the additional:
@Context UriInfo
@Context HttpHeaders
public class AddPetParams {
@javax.ws.rs.core.Context
public javax.ws.rs.core.HttpHeaders coreHttpHeaders;
public AddPetParams coreHttpHeaders(
final javax.ws.rs.core.HttpHeaders coreHttpHeaders
) {
this.coreHttpHeaders = coreHttpHeaders;
return this;
}
@javax.ws.rs.core.Context
public javax.ws.rs.core.UriInfo coreUriInfo;
public AddPetParams coreUriInfo(
final javax.ws.rs.core.UriInfo coreUriInfo
) {
this.coreUriInfo = coreUriInfo;
return this;
}
}
Generate the Request and Response java objects with the Json-b
annotations:
@JsonbProperty
@JsonbCreator
@JsonbPropertyOrder
toString()
equals
hashCode
- Inner static
Builder
class and utility methods
COMING SOON: Using immutables.org library
/**
* Pet
*/
@javax.json.bind.annotation.JsonbPropertyOrder({
Pet.JSON_PROPERTY_ID,
Pet.JSON_PROPERTY_CATEGORY,
Pet.JSON_PROPERTY_NAME,
Pet.JSON_PROPERTY_PHOTO_URLS,
Pet.JSON_PROPERTY_TAGS,
Pet.JSON_PROPERTY_STATUS
})
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.MicroGen")
@io.quarkus.runtime.annotations.RegisterForReflection
public final class Pet {
private final java.util.OptionalLong id;
public static final String JSON_PROPERTY_ID = "id";
@javax.validation.Valid
private final java.util.Optional<Category> category;
public static final String JSON_PROPERTY_CATEGORY = "category";
@javax.validation.constraints.NotNull
private final String name;
public static final String JSON_PROPERTY_NAME = "name";
@javax.validation.constraints.NotNull
private final java.util.List<String> photoUrls;
public static final String JSON_PROPERTY_PHOTO_URLS = "photoUrls";
@javax.validation.Valid
private final java.util.Optional<java.util.List<Tag>> tags;
public static final String JSON_PROPERTY_TAGS = "tags";
private final StatusEnum status;
public static final String JSON_PROPERTY_STATUS = "status";
@javax.json.bind.annotation.JsonbCreator
public Pet(
@javax.json.bind.annotation.JsonbProperty("id")
final java.util.OptionalLong id,
@javax.json.bind.annotation.JsonbProperty("category")
final java.util.Optional<Category> category,
@javax.json.bind.annotation.JsonbProperty("name")
final String name,
@javax.json.bind.annotation.JsonbProperty("photoUrls")
final java.util.List<String> photoUrls,
@javax.json.bind.annotation.JsonbProperty("tags")
final java.util.Optional<java.util.List<Tag>> tags,
@javax.json.bind.annotation.JsonbProperty("status")
final StatusEnum status
) {
this.id = id;
this.category = category;
this.name = name;
this.photoUrls = photoUrls;
this.tags = tags;
this.status = status;
}
// getter omitted
// more details explained below
}
public static Builder builder() {
return Builder.create();
}
public static final class Builder {
private java.util.OptionalLong id;
private java.util.Optional<Category> category;
private String name;
private java.util.List<String> photoUrls;
private java.util.Optional<java.util.List<Tag>> tags;
private StatusEnum status;
private Builder() {
}
public static Builder create() {
return new Builder();
}
public Builder setId(final java.util.OptionalLong id) {
this.id = id;
return this;
}
// more info omitted
}
Generating all the enum values with the @JsonbSerializer
's and @JsonbDeserializer
's
@javax.json.bind.annotation.JsonbTypeSerializer(Pet.StatusEnumSerializer.class)
@javax.json.bind.annotation.JsonbTypeDeserializer(Pet.StatusEnumDeserializer.class)
public static enum StatusEnum {
AVAILABLE("available"),
PENDING("pending"),
SOLD("sold");
private final String value;
StatusEnum(final String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
@Override
public String toString() {
return String.valueOf(this.value);
}
@javax.json.bind.annotation.JsonbCreator
public static StatusEnum fromValue(final String text) {
return java.util.Arrays.stream(StatusEnum.values())
.filter(b -> java.util.Objects.equals(String.valueOf(b.value), text))
.findFirst()
.orElse(null);
}
}
public static class StatusEnumSerializer implements javax.json.bind.serializer.JsonbSerializer<StatusEnum> {
public StatusEnumSerializer() { }
@Override
public void serialize(
StatusEnum aEnum,
javax.json.stream.JsonGenerator jsonGenerator,
javax.json.bind.serializer.SerializationContext serializationContext) {
jsonGenerator.write(aEnum.value);
}
}
public static class StatusEnumDeserializer implements javax.json.bind.serializer.JsonbDeserializer<StatusEnum> {
public StatusEnumDeserializer() {}
@Override
public StatusEnum deserialize(
javax.json.stream.JsonParser jsonParser,
javax.json.bind.serializer.DeserializationContext deserializationContext,
java.lang.reflect.Type type) {
return StatusEnum.fromValue(jsonParser.getString());
}
}
Execute this command
$ mvn archetype:generate \
-DgroupId=com.example \
-DartifactId=example \
-DarchetypeGroupId=io.microgen \
-DarchetypeArtifactId=microgen-archetype
Check what is created
$ cd example && tree . --dirsfirst
example
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── JAXRSConfiguration.java
│ ├── resources
│ │ └── META-INF
│ │ ├── microprofile-config.properties
│ │ └── openapi.yml
│ └── webapp
│ └── WEB-INF
│ └── beans.xml
├── Dockerfile
├── README.md
├── mvnw
├── mvnw.cmd
└── pom.xml
9 directories, 9 files
Implement the JAX-RS API contract
@Path("v1")
@ApplicationScoped
public class PetResource implements PetApi {
@Override
public CompletionStage<Response> addPet(Pet pet) throws WebApplicationException {
return CompletableFuture.completedFuture(
Response.noContent().build()
);
}
@Override
public CompletionStage<Response> getPetById(GetPetByIdParams params) throws WebApplicationException {
return CompletableFuture.completedFuture(
Response.ok(
Pet.builder()
.setId(OptionalLong.of(1))
.setName("pet")
.build()
).build()
);
}
Run and enjoy
$ ./mvnw clean install
$ docker rm -f example || true && docker run -d -p 9080:9080 --name example com.example/example:1.0-SNAPSHOT
$ curl -s -X GET -H "Accept: application/json" http://localhost:9080/api/v1/pet/1 | jq .
{
"category": {
"id": 1,
"name": ""
},
"id": 1,
"name": "name",
"status": "available"
}
Library | Version | License |
---|---|---|
Eclipse JakartaEE | 8.0.0 | |
Eclipse MicroProfile | 3.0 | |
OpenAPI Tools | 4.1.1 | |
Immutables | 2.7.5 |