dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

aglioでAPI Blueprintを使ったドキュメント作成環境をローカルに構築する

(2016/11/24追記)

少々アップデートしました。

(追記ここまで)


以前、API ドキュメンテーションツールを色々検討していて Apiary.io を使ってみたんですが

その時一緒に調べて気になっていた aglio というツールも試してみたところ、
どうやら自分にはこちらの方が合ってる気がしたのでインストール方法などメモ。

参考にさせていただいた記事

使用するツールはすべて npm でインストール可能なものだが、環境構築については gulp を使う。
gulpfile.js についてはほとんどこちらのコピー。

API 設計ドキュメントをGitHubのプルリクでレビューしつつ Apiary みたいに使うには - Qiita

特に、md (markdown) ファイルを複数ファイルに分割して管理する方法は非常に参考になりました。

使うツール

  • aglio
    • HTML ドキュメント生成ツール
  • gulp-aglio
    • aglio を gulp で使えるようにする
  • api-mock
    • モックサーバ
  • dredd
    • テスティングツール
      • API ドキュメントと実際の API との間に齟齬がないことをテストするツール(らしい)
      • が、まだ試せてないのでこれは今後の課題

作ったもの(GitHub)

肝心の API ドキュメントはまだちゃんとしたの作ってませんが、環境構築したものはこちらに。

生成されるドキュメントはこんな感じ。

f:id:dackdive:20150807180941p:plain

ここ から見られます。
https://htmlpreview.github.io/ というサービス初めて知った。。。

最終的な環境

最終的にインストールが必要なパッケージはこのようになった。

$ npm install -g gulp
$ npm install gulp --save-dev
$ npm install gulp-aglio --save-dev

# リアルタイムプレビューに必要
$ npm install browser-sync --save-dev
$ npm install gulp-rename --save-dev

# 複数ファイル分割に必要
$ npm install rimraf --save-dev
$ npm install gulp-ejs --save-dev

# モックサーバに必要
$ npm install -g api-mock

# テスティングツール
$ npm install -g dredd

package.json, gulpfile.js などは以下の通り。
--save-dev でローカルにインストールしているものについては、↓の package.json を使うと楽です)

環境構築までの道のり

最終的な gulpfile.js に至るまでの経緯を。

Step 1 : とりあえず gulp-aglio を使えるようにする

gulp-aglio さえ入っていれば OK。
公式ページとかを見ると、gulp で aglio を使うタスクの最小構成はこんな感じらしい。

var aglio = require('gulp-aglio');
gulp.task('docs', function () {
  gulp.src('_docs/*.md')
    .pipe(aglio({ template: 'default' }))
    .pipe(gulp.dest('docs'));
});

_docs ディレクトリに含まれているすべての *.md ファイルを html に変換し、docs ディレクトリに配置する。
これだけだと 個々の .md ファイルはそれぞれ別の html ファイルになる ことに注意。


Step 2 : リアルタイムプレビューできるようにする

生成した html ファイルをローカルサーバーでプレビューしつつ、md ファイルの更新にあわせてリアルタイムで反映されるようにする。
そのためには browser-sync を使う。

リアルタイムプレビューする html ファイルは index.html というファイル名でなくてはならない ため
gulp-rename も一緒に導入する。

var gulp = require('gulp'),
    aglio = require('gulp-aglio'),
    browserSync = require('browser-sync');
    rename = require('gulp-rename');

var reload = browserSync.reload;

var TEMPLATE_FILES = ['_docs/*.md'],
    PUBLISHED_DIR = 'docs';

gulp.task('docs', function () {
  gulp.src('_docs/*.md')
    .pipe(aglio({ template: 'default' }))
    .pipe(rename('index.html'))
    .pipe(gulp.dest('docs'));
});

gulp.task('watch', function () {
  gulp.watch(TEMPLATE_FILES, ['docs', reload]);
});

gulp.task('browserSync', function() {
  browserSync({
    logConnections: true,
    logFileChanges: true,
    notify: true,
    port: 8088,
    open: false,
    server: {
      baseDir: PUBLISHED_DIR
    }
  });
});

gulp.task('default', ['docs', 'watch', 'browserSync']);

ここでもまだ、_docs/ ディレクトリ以下の md ファイルを 1 つにまとめて index.html を生成するようにはできていないため
扱うことができる md ファイルは 1 つ。
(複数の md ファイルを _docs ディレクトリに配置しているとどれかの内容が index.html として出力される、はず)


Step 3 : md ファイルを複数ファイルに分割できるようにする

開発しているアプリケーションが大規模になると、リソース単位で md ファイルを分割して
たとえばディレクトリを分けて配置したい、と思うようになってくる。
そのため、今度は md ファイルの分割に対応する。

最終的にできた gulpfile.js は上に書いたものなので省略。

詳しくは GitHub のサンプルを確認いただきたいが、LAYOUT_FILE (ここでは apidocs/layout.md)に

FORMAT: 1A

<% include api.md %>
<% include questions.md %>

という形式で include したい md ファイルを並べるとその順に include され、
最終的に1つになった md ファイルが PUBLISHED_DIR(ここでは published)に index.md として出力される。
それを index.html ファイルに変換するので、リアルタイムプレビューはこれまで通り動く。

実は aglio にも複数に分割した md ファイルを include する方法はあって、
https://github.com/danielgtaylor/aglio#including-files

たとえば layout.md に

<!-- include(filename.md) -->

と書いてあると同じようにファイルを include してくれる。

ただ、これだと最終的に1つに結合された md ファイルは生成されず、
後述する モックサーバ用のツール api-mock では複数の md ファイルの include には対応していない (たぶん)ため
↑の方法で 1 つにまとめた md ファイルを生成しておいた方が何かと都合が良い。

Step 4 : モックサーバを起動できるようにする (api-mock)

ツールの導入と利用方法だけ紹介。
モックサーバの起動には api-mock を使う。

インストール後、Step3 で生成した index.md を指定して api-mock を実行すると

$ api-mock published/index.md
info:    Enabled Cross-Origin-Resource-Sharing (CORS)
info:           Allow-Origin: *
info:           Allow-Methods: GET, PUT, POST, PATCH, DELETE, TRACE, OPTIONS
info:           Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, Referer, Prefer
info:    Listening on port 3000

などと表示されるため、http://localhost:3000 に対して GET や POST をするとドキュメントに記載したレスポンスが返る。

f:id:dackdive:20150807181502p:plain

$ curl http://localhost:3000/questions
[
    {
        "question": "Favourite programming language?",
        "published_at": "2014-11-11T08:40:51.620Z",
        "url": "/questions/1",
        "choices": [
            {
                "choice": "Swift",
                "url": "/questions/1/choices/1",
                "votes": 2048
            }, {
                "choice": "Python",
                "url": "/questions/1/choices/2",
                "votes": 1024
            }, {
                "choice": "Objective-C",
                "url": "/questions/1/choices/3",
                "votes": 512
            }, {
                "choice": "Ruby",
                "url": "/questions/1/choices/4",
                "votes": 256
            }
        ]
    }
]


Step 5 : テスティングツール dredd を使う

dredd についてはまだ試せてないので TODO。

リファレンス