【Spring Boot】OpenAPI GeneratorのType Mappingで自作のドメインオブジェクトを使用する
Spring Boot + OpenAPI GeneratorでTodoリストのAPIサーバを作っている。OpenAPIでエンドポイントやリクエスト・レスポンスを定義するところは楽だったが、 OpenAPIで定義したリクエスト・レスポンスの型は自動で生成されたコードの中に定義されることがわかった。そのため、Spring Boot側でドメインオブジェクトを定義しても、それをそのままリクエストやレスポンスの型として使うことができない。
自分はGeneration Gapパターンを使って開発しているので、自動生成されたコードには手を加えたくないし、gitの管理下にも入れたくない。そのためSpring Bootの側でConverterを用意するしかないかと思っていたのだが、調べてみるとOpenAPI GeneratorのType Mappingという機能で賄えそうだった。
Type Mappingとは
OpenAPIで定義した型と、実装コード側で定義した型を変換することができる機能。設定項目は以下の2つがある。
--type-mappings
: 変換先の型を指定する。--import-mappings
: 変換に伴いインポートする型のテンプレートを指定する。
たいていの場合、両方を指定する必要がある。
Type Mappingを使う
今回作っているのは簡単なTodoリストのRESTful APIであるため、SpringBoot側で定義したドメインを、そのままCREATE時のレスポンスの型として使いたい。
OpenAPI側では、レスポンスを Task
型として以下のように定義した。
paths: /tasks: post: summary: add a new task operationId: addTask requestBody: description: task to create content: application/json: schema: $ref: '#/components/schemas/TaskRequest' responses: '201': description: created content: application/json: schema: $ref: '#/components/schemas/Task' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Error'
components: schemas: Task: description: one task object type: object allOf: - $ref: '#/components/schemas/TaskRequest' - required: - id - done properties: id: type: integer format: int64 done: type: boolean TaskRequest: description: object to create or edit a new task type: object required: - content - urgency - importance properties: content: type: string urgency: type: integer format: int32 importance: type: integer format: int32
この結果、OpenAPI Generatorによって以下のようなメソッドが生成される。 ResponseEntity<Task>
が戻り値となっており、この Task
型も Task.java
として生成されている。
default ResponseEntity<Task> addTask(@ApiParam(value = "task to create" ) @Valid @RequestBody(required = false) TaskRequest taskRequest) { ... }
一方、Spring Boot側では、以下のようなドメインオブジェクトを定義した。
@Entity @Table(name="task") @Data @NoArgsConstructor @AllArgsConstructor public class TaskEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String content; private int urgency; private int importance; private boolean isDone; public TaskEntity(String content, Integer urgency, Integer importance, boolean isDone) { this.content = content; this.urgency = urgency; this.importance = importance; this.isDone = isDone; } }
始めは自動生成された Task
型のみでサーバサイドの処理を行えないかと思ったが、Entityとして管理されているドメインオブジェクトを使わなければJpaをうまく使うことができない。
そこで、Type Mappingで変換をかける。 build.gradle
に以下の設定を追加する。
openApiGenerate { typeMappings = [ Task: 'com.example.springboottodo.TaskEntity' ] importMappings = [ Task: 'com.example.springboottodo.TaskEntity' ] {
この状態で再度OpenAPI Generatorでコードを生成してみると、 addTask
メソッドの返り値の型が変わった。
default ResponseEntity<com.example.springboottodo.TaskEntity> addTask(@ApiParam(value = "task to create" ) @Valid @RequestBody(required = false) TaskRequest taskRequest) { ... }
これで、直接 TaskEntity
型を使ってレスポンスを作れるようになる。
なお、最初に生成された Task
型はコード内では使われなくなるが、OpenAPIからドキュメントを生成するときは Task
型に基づいた記載となる。