esa から Confluence へのデータを移行するプログラムを作成してみました。

Google 先生から様々な方のプログラムを見つけてくることが出来ますが、こちらは、集大成版になります。

やりたいこと

esa から 「エクスポート」で出力した md ファイル を

Confluence の Rest API を用いて、Confluenceのスペース宛に入れます。

言語

ConfluenceのAPIを叩けばいいので、様々な言語で行えます。

  • Ruby
  • Python
  • PHP

などで作成して紆余曲折を経ましたが、最終的には

node.js ( Javascript )

を用いて実装しました。

どの言語でも大丈夫そうなのですが、こちら実際にやってみると、様々なライブラリの癖に悩まされることになります。

今回やりたいことが全て、そして綺麗に出来たので、node.js を採用しました。

esaの固有の問題

「esaからエクスポートされたマークダウンファイルが、完全なマークダウンのデータではない」

ことが、esaのデータファイル固有の問題となります。

esaは大変柔軟な表現力を持っています。md ( マークダウン ) と、HTMLを両方同時に記述し、表現することが出来ます。それゆえ、出力された記事ファイルには、マークダウンと、普通のHTMLが含まれています。

それだけであればいいのですが、このHTMLが、大変不完全なものでも問題なく表示されるようになっています。

esaでは表示時にマークダウンだけ対応し、HTMLはそのまま出力しているのでしょう。ブラウザは不完全なHTMLでも表示することができるのです。

しかしConfluenceはXHTML準拠の独自規格になります。これは不完全なHTMLを全く受け付けません。

例えばこちらをみる限り、一行目はspanタグが閉じられていません。二行目は、aタグが始まっていないのに、閉じています。しかしこれはesaでは普通に表示されます。エラーは発生しません。

恐ろしいですね、ブラウザの曖昧さ加減。驚愕します。

当然ながら、Confluebnceではエラーです。

必要な機能

必要な機能を簡単に切り出します。

・マークダウンを正しく変換し、正しくXHTMLに正規化する

・Confluenceに正しくデータを入れる

その上、今回は、画像もなんとかします。ですので、アップロード済みの内容を読み取り、さらに画像URLを抽出し、画像をページにアップロードして、アップロードした後のURLに書き換えて再度保存する。

をやる必要があります。これを分解すると、

・アップロード済みの内容を読み取る。

・画像URLを抽出する

・画像データをページにアップロードする。

・再保存する。

手順が必要ですね。

機能

全体の構成をまるごと掲載はしません。ですが、特徴的ないくつかの機能ごとに、どのように行うか、関数を掲載していきます。

実際には、これらの関数をきちんと呼び出すために、いかにプログラムを構成するかが、テクニカルなところだと思います。javascriptは基本的に非同期で、便利なのですが、うまく扱うことが必要です。

confluenceの記事を、正しいXHTMLにする。

これには様々なやり方がありますが、こちらの方法で、ある程度、きちんとしたものが完成します。

お試しするとわかりますが、いくつか完璧ではないです。markdown-itというライブラリを利用していますが、そのリンク自動検出が壊れている影響です。執筆時点では、この不具合は直っていないようでしたので、最低限、img タグのURLは直しています。また、タスクリストについて変換し、Confluenceでは使えないタグも除去しています。

このライブラリは、僕が様々な言語で試した中では、一番綺麗にマークダウンと、XHTML正規化を行うライブラリです。

また、制御文字が入っていても、Confluenceが拒否するので、これを除去します。(ブラウザではもちろんエラーが起きません)

Confluenceに記事をUPLOADする

記事をきちんとUPLOADするには、

・すでに同名の記事があるか調べる

 ・記事があれば、そのIDを取得する。

  ・ID取得できたなら、そのバージョンを取得する

   ・上書きする

 ・なければ、そのまま記事を追加する

というフローになります。

Confluenceはスペース内部の記事名が、重複することが出来ません。それゆえ、記事タイトル名がkeyになります。

さらに、上書きするには、その記事のバージョン情報を事前に取得し、きちんとインクリメントする必要があります。

このコードでは、updatePage( “タイトル”, “HTMLデータ”, 紐づける親ID, 完了後に実行する関数) と呼び出すだけで、全て行います。

confluenceApiUrl、userName、bearerToken、spaceNameはそれぞれ、自分のConfluenceのURL、ユーザー名(メールアドレス) トークン(Conflueneの、ユーザーに紐づいているトークン)、スペース名です。

axios というライブラリを利用しています。

ページ内の画像URLから、ページに画像をUPLOADする。

今回の場合、画像はURLさえわかれば誰でも見れる場所にあります。

もしそうでない場合は、認証情報をつけて画像を取得する必要があります。このコードでは、ローカルにDLせず、そのままstreamで受け取り、ページにUPLOADします。

注意点ですが、このContent-Typeをこのように設定しないと、失敗します。

あるスペース内にある、全ての記事を見つけ出す。

最後に、あるスペース内にある、全ての記事を取得するコードです。

これと上記の関数を組み合わせることで、Confluenceの記事の書き換えができますね。

この関数は扱いにコツがあり、ご覧の通り、再起的にこのfindPageRecursiveを呼ぶことを想定しています。渡された関数の第三引数としてnextURLが渡されますので、これを利用して、再度同じ関数を呼びます。すると、次のページが取得できます。

移行はイバラの道

一見簡単そうなこのデータ移行ですが、完全に行うには、様々な知見が必要になります。

どこまでの完全性と、コストを取るべきか、最初に考える方がいいでしょう。

ちなみに、全てのタグを削除して、文字データだけ取得すると、ほとんどの苦労はなくなります。

最終更新: 2023年10月22日