😽

API BlueprintのファイルをSwaggerファイルに変換する

2022/12/13に公開

この記事は?

APIドキュメントの記述をAPI BlueprintからSwaggerに切り替えたい方へ向けて既存のapibファイルをSwagger向けのyamlファイルに変換する方法を記録します。

背景

元々使っていたツール

  • API Blueprint
    • APIドキュメントの作成
  • aglio
    • 複数のBlueprintファイルを統合してUI化するためのライブラリ

リプレイスに至った経緯

  • モックサーバーの設定
  • APIリクエスト/レスポンスの型定義
  • バックエンドのAPI設計

上記のようなことをAPIドキュメントで自動化したいよねー、という話がチーム内で浮上したことを切っ掛けに
そもそもBlueprintは2015年にリリースされて以降メンテナンスされていないし、サードパーティ製ライブラリも発展していないし、Markdown記法だから独自ツールでゴチャゴチャするのも面倒くさいし、といった問題がボロボロと……。
OSSも比較的活発に動いていて拡張性が高く、JSONやYAMLで記述しやすいツールとしてSwaggerへ置き換えることになりました。

本題:BlueprintのファイルをSwagger用に変換

Docker上で自動的に変換を行うことやグローバルインストールしたNode Moduleを用いてコマンドを実行することも可能ですが、
いずれBlueprint関連のファイルは不要になることと、変換処理を細かくカスタマイズしたいことを踏まえて
好きなタイミングで処理を実行できるNode.jsでスクリプトを組むことにします。

使ったNPMパッケージ

  • apib2swagger
    • apibファイルをSwagger向けのJSONに変換するために使用
  • glob
    • 作成済みのapibファイルを一括検索するために使用
  • json-to-pretty-yaml
    • apib2swaggerで生成したJSONをYamlに変換するために使用

ステップ1:対象ファイルの検索

大抵の場合apibファイルは何らかのディレクトリにまとめて配置されているはずなので、下記のようなディレクトリ構成になっている想定です。

/root
  /blueprint
  ┣ group-a
  ┃   ┣ api_doc_a.apib
  ┃   ┗ api_doc_b.apib
  ┗ group-b
      ┣ api_doc_c.apib
      ┗ api_doc_d.apib

これらのファイルをrootディレクトリからglobを使って探索します。
ついでに、出力用のパスも用意しておくと後々便利です。

/** 入力するapibファイルのパス配列 */
const inputPaths = glob.sync(resolve(__dirname, 'blueprint/**/*.apib'))

/**
 * 入出力ファイルパスの配列
 * swaggerディレクトリにYAML形式で出力する場合の記述です。
 * 以降の処理はこちらを使います。
 */
const paths = inputPaths.map((input) => {
  input,
  output: input
    .repace('/blueprint/', '/swagger/')
    .replace(/\.apib$/, '.yml'),
})

ステップ2:apibファイルからJSONオブジェクトを生成

ステップ1で取得した入力パス(input)のファイルを読み込んで、apib2swaggerを使ってJSONオブジェクトに変換します。

const fs = require('fs')
const { convert } = require('apib2swagger')

for (const { input } of paths) {
  const apibRaw = fs.readFileSync(input)
  convert(apibRaw.toString(), { preferReference: true }, (error, result) => {
    // `result.swagger`でJSONオブジェクトを取得可能
  })
}

apib2swaggerが提供している関数はconvertは文字列形式でapibのテキスト情報を受け取って、JSONオブジェクトとしてswagger用の情報を出力することにご注意ください。
(型定義の記載箇所が見つからず苦労しました……)

ステップ3:JSONオブジェクトをYAMLテキストに変換して出力

ステップ2で取得したJSONオブジェクトをYAML形式に変換してファイル出力します。
そのまま出力しようとするとディレクトリの参照エラーになってしまうので、ついでにディレクトリを用意しておきましょう。

const fs = require('fs')
const { convert } = require('apib2swagger')
const { stringify } = require('json-to-pretty-yaml')

for (const { input, output } of paths) {
  const outputDir = dirname(output)
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true })
  const apibRaw = fs.readFileSync(input)
  convert(apibRaw.toString(), { preferReference: true }, (error, result) => {
    const yamlText = stringify(result.swagger)
    fs.writeFileSync(output, yamlText)
  })
}

JSON形式のまま使用したい場合はresult.swaggerを文字列に変換して出力します。

const fs = require('fs')
const { convert } = require('apib2swagger')

for (const { input, output } of paths) {
  const outputDir = dirname(output)
  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true })
  const apibRaw = fs.readFileSync(input)
  convert(apibRaw.toString(), { preferReference: true }, (error, result) => {
    const jsonText = JSON.stringify(result.swagger)
    fs.writeFileSync(output, jsonText)
  })
}

後は、上記のスクリプトをjsファイルに記載して、必要に応じてエラーハンドリングを加えてからNode.jsで実行すればswagger用のファイルが吐き出されます。

余談

Blueprintは2015年以降更新されていない一方で、apib2swaggerは2022年にも更新があったことが意外でした。
どこも最近までBlueprintを使い続けていてSwaggerに置き換えるツールは一定の需要があるのかも知れません。
とはいえ、出力されるYamlが対応しているOpenAPIのバージョンが2系だったので、最新の3系の記法を活用するには手作業で修正を加えるか、別途独自のスクリプトを組む必要があります。

あと、json-to-pretty-yamlが5年以上更新されていないことも意外でした。
最近は別のツールが主流になっているのでしょうか?ご存知の方がいればご教示いただきたいです。
まあ、機能はシンプルですし、こういったリプレイスのタイミングくらいでしか出番がないから活発になりようもないのかも……?

Discussion