7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RCC (立命館コンピュータークラブ)Advent Calendar 2024

Day 4

Swaggerのファイルを分割して管理、redoclyで結合しよう! [swagger, redocly, GitHubActions]

Last updated at Posted at 2024-12-04

swaggerを書くときにレスポンスのjsonなど使いまわしたいものをコンポーネント化したいと思ったことありませんか?

今回はredoclyを使ってswaggerのファイル統合を使って、swaggerで記述するものを分割していこうと思います。

swaggerに関する記事

swaggerはこちらの記事で紹介しています。

redoclyとは

redoclyは、openAPIファイルの生成、管理を行えるCLIツールです。

インストールはnpmで行います。

zsh
npm install -g @redocly/cli

下のコマンドでversionが表示されたらインストール成功です。

zsh
redocly --version

dockerイメージを使ってコンテナで動かすこともできますが、後ほど紹介します。

swaggerを分割・結合するメリットや必要性

機能ごとにファイルを分けられる

サインイン、サインアップ、ユーザーの取得など、機能ごとにファイルを分けることができるので、管理の効率性を上げることができます。

IDEで確認できる

私はJetbrainsのIDEを使っていますが、IDEにはswaggerのプラグインが整備されていることが多いので、統合したファイルかあらプレビューすることができます。

上記で紹介したコンポーネント毎にファイルを分けてしまうとIDEが読み込むことができないので結合する必要があります。

image.png

コード生成に必要

近年ではドキュメント駆動設計でswaggerのファイルからjsonやcontroller層で書くコードを生成させて開発させるという方法を採用して開発してる方がかなり多いです。

その場合でもswaggerのファイルにすべての情報が明記されてある必要があるので、結合する必要があります。

結合するファイル

今回統合するファイルは、/docs/swaggerというディレクトリを作り、下のように分けました。

/docs/swagger/
.
├── components
│   └── todo.yml
├── paths
│   └── todo.yml
└── root.swagger.yml

ディレクトリは以下のように分けています。

ディレクトリ名 役割
components jsonのschemaなどの使い回すものを定義
paths 一つのURIに対してのapi定義を記述
root.swagger.yml URI名を設定してpathsに定義したものを割り当て

実際に書いたコードは下のようになっています。

componentsのファイル

/docs/swagger/components/todo.yml
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のファイル

/docs/swagger/paths/todo.yml
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

統合ファイル

/docs/swagger/root.swagger.yml
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で下のコマンドを打ちます。

zsh
redocly bundle ./root.swagger.yml --output=./generated.gen.swagger.yml

こうすることで下のようなコードが生成されます。

generated.gen.swagger.yml
/docs/swagger/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

こうすることでIDE上でファイルが見れるので便利になります。

スクリーンショット 2024-11-14 0.59.16.png

JetbrainsのIDEで見ています。

dockerでコマンドを動かす

npmでインストールしたくない場合は、dockerイメージを使うこともできます。

下のコマンドで行うことができます。

zsh
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を書きます。

.github/workflows/swagger.yml
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を使った記事です

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?