CodepaLOUsa 2019 - Presentations
You can find the slide decks and collateral from my CodepaLOUsa 2019 session here.
Contract-First API Development from Deven Phillips
Start with an example JSON object:
{
"title": "Something I need to do",
"description": "A long description",
"dueDate": "2019-09-14T09:00:00.000Z",
"complete": false,
"created": "2019-09-27T14:00:00.000Z",
"id": "543da998-a186-11e9-96b9-2b86a7060fde"
}
- Edit the API
- Click on “Add a data type”
- Give it a name and paste the example JSON data
- Select “REST Resource” and click “Save”
- Show OpenAPI YAML Source
- Explain about creating a “Request” type and a “Hydrated” type.
- Request type would not have “id” or “created”
- Explain “allOf” and “$ref”
- Explain about inheritance and discriminator
- Download the OpenAPI YAML
- Install OpenAPI Generator
- npm install -g openapi-generator
Generate Server Implementation using JAX-RS
- Show “openapi-generator help generate”
- Create configuration JSON file
{
"apiPackage": "com.redhat.labs.todo.api",
"artifactId": "todo",
"artifactVersion": "1.0.0-SNAPSHOT",
"groupId": "com.redhat.labs",
"packageName": "com.redhat.labs.todo",
"modelPackage": "com.redhat.labs.todo.models",
"withXml": true,
"dateLibrary": "java8",
"java8": true,
"booleanGetterPrefix": "is",
"serverPort": 8080
}
|
- Run generator
- openapi-generator generate -i ~/Downloads/todo_2.yml -g jaxrs-jersey -c todo-api-config.json -o todo-jax-rs
- Open new application in VSCode
- Explain about shortcomings in OpenAPI Generator
- Inheritence
- Annotations/DAO/Database
- Add manually written code
- Implement ToDo JPA Entity
package com.redhat.labs.todo.models;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "todos")
public class ToDoImpl extends Todo {
@Id
public UUID getId() {
return super.getId();
}
}
|
- Show ToDoApiServiceImpl and how you would implement business logic
Generate Client SDK Using typescript-axios
- Create generator config
{
"modelPackage": "models",
"apiPackage": "api",
"modelPropertyNaming": "camelCase",
"supportsES6": true,
"npmName": "todo-ts-sdk",
"npmVersion": "1.0.0",
"snapshot": true,
"withSeparateModelsAndApi": true
}
|
- Generate Client SDK:
- openapi-generator generate -i ~/Downloads/todo_2.yml -g typescript-axios -c todo -ts-sdk-config.json -o todo-typescript-axios
- Open Client SDK in VSCode
- Show models
- Show how to use the API in an application
Generate Spring application from OpenAPI
- Show “openapi-generator help generate”
- Create configuration JSON file
{
"artifactId": "todo",
"artifactVersion": "1.0.0-SNAPSHOT",
"groupId": "com.redhat.labs",
"packageName": "com.redhat.labs.todo",
"basePackage": "com.redhat.labs.todo",
"apiPackage": "com.redhat.labs.todo.api",
"modelPackage": "com.redhat.labs.todo.models",
"configPackage": "com.redhat.labs.todo.configuration",
"withXml": true,
"dateLibrary": "java8",
"java8": true,
"booleanGetterPrefix": "is",
"serverPort": 8080,
"modelNameSuffix": "Base",
"minimalUpdate": true,
"library": "spring-boot"
}
|
- Run generator
- openapi-generator generate -i ~/Downloads/todo_2.yml -g spring -c todo-spring-config.json -o todo-spring
- Open new application in VSCode
- Explain about shortcomings in OpenAPI Generator
- Inheritence
- Annotations/DAO/Database
- Add spring-boot-starter-data-jpa to dependencies
- Add manually written code
- Implement ToDo JPA Entity
@Entity
@Table(name = "todos")
public class ToDo extends TodoBase {
private static final Logger LOG = LoggerFactory.getLogger(ToDo.class);
@Override
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public @NotNull UUID getId() {
return super.getId();
}
@Override
@Transient
public String getDiscriminator() {
return super.getDiscriminator();
}
/* … */
public static final ToDo fromTodoRequest(ToDoRequestBase request) {
ToDo newToDo = new ToDo();
newToDo.setComplete(request.isComplete());
newToDo.setDescription(request.getDescription());
newToDo.setDiscriminator("ToDo");
newToDo.setDueDate(request.getDueDate());
newToDo.setTitle(request.getTitle());
LOG.debug(newToDo.toString());
return newToDo;
}
}
|
- This POJO extends the generated POJO which has the Swagger and Jackson annotations. All persisted field getters MUST be overriden even just to call the super() method.
- Implement API methods from Interface
- REMEMBER to copy the parameter annotations except for the Swagger annotations.
@Override
public ResponseEntity<Void> createTodo(@Valid @RequestBody ToDoRequestBase toDoRequestBase) {
ToDo newToDo = ToDo.fromTodoRequest(toDoRequestBase);
ToDo persisted = dao.save(newToDo);
if (persisted.getId() != null) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
|
Alternative OpenAPI Methods
Direct OpenAPI Support
- Reads the OpenAPI spec directly and then you just attach business logic to the operationIds
- Reads the OpenAPI spec and allows you to attach functions/methods to the operationIds
The benefit of direct OpenAPI support is that there is no code generation step which can sometimes be problemmatic.
Comments