swaggerを書くときにレスポンスのjsonなど使いまわしたいものをコンポーネント化したいと思ったことありませんか?
今回はredoclyを使ってswaggerのファイル統合を使って、swaggerで記述するものを分割していこうと思います。
swaggerに関する記事
swaggerはこちらの記事で紹介しています。
redoclyとは
redoclyは、openAPIファイルの生成、管理を行えるCLIツールです。
インストールはnpmで行います。
npm install -g @redocly/cli
下のコマンドでversionが表示されたらインストール成功です。
redocly --version
dockerイメージを使ってコンテナで動かすこともできますが、後ほど紹介します。
swaggerを分割・結合するメリットや必要性
機能ごとにファイルを分けられる
サインイン、サインアップ、ユーザーの取得など、機能ごとにファイルを分けることができるので、管理の効率性を上げることができます。
IDEで確認できる
私はJetbrainsのIDEを使っていますが、IDEにはswaggerのプラグインが整備されていることが多いので、統合したファイルかあらプレビューすることができます。
上記で紹介したコンポーネント毎にファイルを分けてしまうとIDEが読み込むことができないので結合する必要があります。
コード生成に必要
近年ではドキュメント駆動設計でswaggerのファイルからjsonやcontroller層で書くコードを生成させて開発させるという方法を採用して開発してる方がかなり多いです。
その場合でもswaggerのファイルにすべての情報が明記されてある必要があるので、結合する必要があります。
結合するファイル
今回統合するファイルは、/docs/swagger
というディレクトリを作り、下のように分けました。
.
├── components
│ └── todo.yml
├── paths
│ └── todo.yml
└── root.swagger.yml
ディレクトリは以下のように分けています。
ディレクトリ名 | 役割 |
---|---|
components | jsonのschemaなどの使い回すものを定義 |
paths | 一つのURIに対してのapi定義を記述 |
root.swagger.yml | URI名を設定してpathsに定義したものを割り当て |
実際に書いたコードは下のようになっています。
componentsのファイル
TodoID:
name: "taskId"
in: path
description: "詳細を取得したいtodoタスクのID"
required: true
schema:
type: "integer"
format: "int64"
example: 1
Todo:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: "買い物に行く"
deadline:
type: string
format: date
description: 締切日
example: "2024-10-12"
done:
type: boolean
description: タスクの状態
example: false
required:
- id
- task
- deadline
- done
Todos:
type: object
properties:
total:
type: integer
description: 全タスクの総数
example: 2
tasks:
type: array
items:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: "買い物に行く"
deadline:
type: string
format: date
description: 締切日
example: "2024-10-12"
done:
type: boolean
description: タスクの状態
example: false
required:
- id
- task
- deadline
- done
TodoCreate:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: "買い物に行く"
deadline:
type: string
format: date
description: 締切日
example: "2024-10-12"
done:
type: boolean
description: タスクの状態
example: false
required:
- task
- deadline
TodoDetail:
type: object
properties:
task:
type: string
description: タスク
example: "買い物に行く"
deadline:
type: string
description: 締切日
example: "2024-10-12"
required:
- task
- deadline
TodoDetailUpdate:
type: object
properties:
task:
type: string
description: タスクの内容
example: "買い物に行く"
deadline:
type: string
format: date
description: 締切日
example: "2024-10-12"
done:
type: boolean
description: タスクの状態
example: false
required:
- task
- deadline
- done
pathsのファイル
todo_create:
post:
summary: todo-create
description: "todoタスクを作成します"
tags:
- todo
requestBody:
content:
application/json:
schema:
$ref: "../components/todo.yml#/TodoCreate"
responses:
201:
description: success
content:
application/json:
schema:
$ref: "../components/todo.yml#/Todo"
400:
description: Bad Request
500:
description: Internal Server Error
todo_read_all:
get:
operationId: todolist-findall
description: "todoタスクの一覧を取得"
tags:
- todo
responses:
200:
description: "成功時のレスポンス"
content:
application/json:
schema:
$ref: "../components/todo.yml#/Todos"
400:
description: Bad Request
500:
description: Internal Server Error
todo_detail_crud:
get:
operationId: todo-detail
description: "todoタスクの詳細を表示します"
tags:
- todo
parameters:
- $ref: "../components/todo.yml#/TodoID"
responses:
200:
description: "成功時のレスポンス"
content:
application/json:
schema:
$ref: "../components/todo.yml#/TodoDetail"
"400":
description: Bad Request
put:
operationId: todo-update
description: "タスクの内容を更新します"
tags:
- todo
parameters:
- $ref: "../components/todo.yml#/TodoID"
requestBody:
content:
application/json:
schema:
$ref: "../components/todo.yml#/TodoDetailUpdate"
responses:
201:
description: success
400:
description: Bad Request
500:
description: Internal Server Error
delete:
operationId: todo-delete
description: "todoタスクを削除します"
tags:
- todo
parameters:
- $ref: "../components/todo.yml#/TodoID"
responses:
204:
description: success
400:
description: Bad Request
500:
description: Internal Server Error
統合ファイル
openapi: 3.0.0
info:
title: todo
description: todoAPI
version: 1.0.0
tags:
- name: todo
description: todoタスクのAPI
paths:
/tasks/create:
$ref: "./paths/todo.yml#/todo_create"
/tasks:
$ref: "./paths/todo.yml#/todo_read_all"
/tasks/{taskId}:
$ref: "./paths/todo.yml#/todo_detail_crud"
長々していますが、今回はこちらのファイルを使ってみます。
結合する
記述したコードを統合したいと思います。
/docs/swagger
で下のコマンドを打ちます。
redocly bundle ./root.swagger.yml --output=./generated.gen.swagger.yml
こうすることで下のようなコードが生成されます。
generated.gen.swagger.yml
openapi: 3.0.0
info:
title: todo
description: todoAPI
version: 1.0.0
tags:
- name: todo
description: todoタスクのAPI
paths:
/tasks/create:
post:
summary: todo-create
description: todoタスクを作成します
tags:
- todo
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TodoCreate'
responses:
'201':
description: success
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
'400':
description: Bad Request
'500':
description: Internal Server Error
/tasks:
get:
operationId: todolist-findall
description: todoタスクの一覧を取得
tags:
- todo
responses:
'200':
description: 成功時のレスポンス
content:
application/json:
schema:
$ref: '#/components/schemas/Todos'
'400':
description: Bad Request
'500':
description: Internal Server Error
/tasks/{taskId}:
get:
operationId: todo-detail
description: todoタスクの詳細を表示します
tags:
- todo
parameters:
- $ref: '#/components/parameters/TodoID'
responses:
'200':
description: 成功時のレスポンス
content:
application/json:
schema:
$ref: '#/components/schemas/TodoDetail'
'400':
description: Bad Request
put:
operationId: todo-update
description: タスクの内容を更新します
tags:
- todo
parameters:
- $ref: '#/components/parameters/TodoID'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/TodoDetailUpdate'
responses:
'201':
description: success
'400':
description: Bad Request
'500':
description: Internal Server Error
delete:
operationId: todo-delete
description: todoタスクを削除します
tags:
- todo
parameters:
- $ref: '#/components/parameters/TodoID'
responses:
'204':
description: success
'400':
description: Bad Request
'500':
description: Internal Server Error
components:
schemas:
TodoCreate:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: 買い物に行く
deadline:
type: string
format: date
description: 締切日
example: '2024-10-12'
done:
type: boolean
description: タスクの状態
example: false
required:
- task
- deadline
Todo:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: 買い物に行く
deadline:
type: string
format: date
description: 締切日
example: '2024-10-12'
done:
type: boolean
description: タスクの状態
example: false
required:
- id
- task
- deadline
- done
Todos:
type: object
properties:
total:
type: integer
description: 全タスクの総数
example: 2
tasks:
type: array
items:
type: object
properties:
id:
type: integer
description: タスクのID
example: 1
task:
type: string
description: タスクの内容
example: 買い物に行く
deadline:
type: string
format: date
description: 締切日
example: '2024-10-12'
done:
type: boolean
description: タスクの状態
example: false
required:
- id
- task
- deadline
- done
TodoDetail:
type: object
properties:
task:
type: string
description: タスク
example: 買い物に行く
deadline:
type: string
description: 締切日
example: '2024-10-12'
required:
- task
- deadline
TodoDetailUpdate:
type: object
properties:
task:
type: string
description: タスクの内容
example: 買い物に行く
deadline:
type: string
format: date
description: 締切日
example: '2024-10-12'
done:
type: boolean
description: タスクの状態
example: false
required:
- task
- deadline
- done
parameters:
TodoID:
name: taskId
in: path
description: 詳細を取得したいtodoタスクのID
required: true
schema:
type: integer
format: int64
example: 1
JetbrainsのIDEで見ています。
dockerでコマンドを動かす
npmでインストールしたくない場合は、dockerイメージを使うこともできます。
下のコマンドで行うことができます。
docker run -v $(PWD):/spec --rm redocly/cli:latest bundle docs/swagger/root.swagger.yml --output=docs/swagger/generated.gen.swagger.yml
指定しているファイルを相対パスとして記述しているからか、上のコマンドだとredoclyのversionファイルが出力されませんでした。
-vでボリュームを設定することができ、コンテナとローカルでコードを共有することができます。$(PWD)で現在のディレクトリ位置を表示して、それをコンテナ内の/specディレクトリにマウントします。
--rmでコンテナを実行したらコンテナを消すようにできます。
そこから先述したredoclyコマンドを書きます。
実行すると下のように表示されます。
docker run -v /Users/maoz/Documents/application/HubMe:/spec --rm redocly/cli:latest bundle docs/swagger/root.swagger.yml --output=docs/swagger/generated.gen.swagger.yml
(node:1) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
bundling docs/swagger/root.swagger.yml...
📦 Created a bundle for docs/swagger/root.swagger.yml at docs/swagger/generated.gen.swagger.yml 26ms.
GithubActionsでCIを回す。
GithubActionsで結合作業を忘れないよう自動化しましょう。
コマンド長いのと、コマンドを変更した際に変更を適応しやすいので、下のMakefileファイルを書きます。
docker/swagger/bundle:
docker run -v $(PWD):/spec --rm redocly/cli:latest bundle docs/swagger/root.swagger.yml --output=docs/swagger/generated.gen.swagger.yml
こちらはルートディレクトリにおきます。
下のようにCIを書きます。
name: swagger.yml
on:
pull_request:
types:
- opened
- synchronize
- reopened
- closed
branches:
- develop
jobs:
swagger:
name: swagger bundle
permissions:
contents: write
pull-requests: write
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4.2.2
- name: swagger bundle
run: make docker/swagger/bundle
- name: fix swagger
uses: dev-hato/actions-diff-pr-management@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
branch-name-prefix: 'fix-swagger'
ymlについて
今回はdevelopブランチに対してpull_requestを作成した際にCIを回すようにしています。
on:
pull_request:
types:
- opened
- synchronize
- reopened
- closed
branches:
- develop
jobsでactionに関する設定等を記述しています。
permissions
で、GITHUB_TOKENにcontentの修正やpull_requestの作成を許可しています。
timeout-minutes
で、タイムアウトを設定しています。
runs-on
では、実行するOS環境を指定しています。
permissions:
contents: write
pull-requests: write
timeout-minutes: 10
runs-on: ubuntu-latest
stepsでjob内で実行する書く処理を記述しています。
checkoutでは、リポジトリの内容をactionsにクローンしています。
- name: checkout
uses: actions/checkout@v4.2.2
swagger bundleで先ほど作成したmakeコマンドを実行しています。
- name: swagger bundle
run: make docker/swagger/bundle
先ほどのmakeコマンドの結果をactions-diff-pr-management
でPRとして作成して、適応させます。
- name: fix swagger
uses: dev-hato/actions-diff-pr-management@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
branch-name-prefix: 'fix-swagger'
actions-diff-pr-management
に関する記事はこちらになります。
上記のCIを組むことで結合を自動化することができます。
ぜひ参考にしてください!
GithubActions関連の記事
GithubActionsを使った記事です