Knowledge→GROWI 移行 (5)
GROWI API の使い方を調べる
GROWI API の使い方を調べます。ドキュメントは下記のページにあります。
私にはドキュメント読んでも具体的なイメージがわかなかったので、やはり Google で検索すると、私にピッタリなサイトが見つかりました。
まずは、そのまま使わせていただきます。requests モジュールが必要なので、.devcontainer/python/requirement.txt に requests を追加してコンテナを再ビルドしました。
そういえば、以前 PostgreSQL にアクセスするのに psycopg2 モジュールを使いました。この時も .devcontainer/python/requirement.txt に psycopg2 を追加してコンテナを再ビルドしました。
簡単に動かしてみます
from crclient import CrClient crclient = CrClient('growi', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', 'tiger', './knowledge_to_growi/contents') crclient.fetch()
確認用に記事を 1 件だけ登録してあるのですが、なんか取得できているようです。
# cd /workspace ; /usr/bin/env /usr/local/bin/python3.8 /root/.vscode-server/extensions/ms-python.python-2021.12.1559732655/pythonFiles/lib/python/debugpy/launcher 43313 -- /workspace/knowledge_to_growi/crclient_sample.py skip item: {'_id': '61f9120a32a52003888290b5', 'status': 'published', 'grant': 1, 'grantedUsers': [], 'liker': [], 'seenUsers': ['61f7bcc6834bc4803f0e2bfa'], 'commentCount': 0, 'createdAt': '2022-02-01T10:57:14.468Z', 'updatedAt': '2022-02-01T10:57:14.488Z', 'path': '/user/tiger/自分自身のこと', 'creator': '61f7bcc6834bc4803f0e2bfa', 'lastUpdateUser': {'_id': '61f7bcc6834bc4803f0e2bfa', 'isGravatarEnabled': False, 'isEmailPublished': True, 'lang': 'ja_JP', 'status': 2, 'admin': False, 'createdAt': '2022-01-31T10:41:10.624Z', 'username': 'tiger', 'email': 'tiger@example.com', 'lastLoginAt': '2022-02-01T10:49:07.549Z', 'imageUrlCached': '/images/icons/user.svg', 'name': 'tiger62shin'}, 'redirectTo': None, 'grantedGroup': None, '__v': 1, 'revision': '61f9120a32a52003888290b9'} skip item: {'_id': '61f7bd73834bc4803f0e2c25', 'status': 'published', 'grant': 1, 'grantedUsers': ['61f7bcc6834bc4803f0e2bfa'], 'liker': [], 'seenUsers': ['61f7bcc6834bc4803f0e2bfa'], 'commentCount': 0, 'createdAt': '2022-01-31T10:44:03.214Z', 'updatedAt': '2022-01-31T10:44:03.239Z', 'path': '/user/tiger', 'creator': '61f7bcc6834bc4803f0e2bfa', 'lastUpdateUser': {'_id': '61f7bcc6834bc4803f0e2bfa', 'isGravatarEnabled': False, 'isEmailPublished': True, 'lang': 'ja_JP', 'status': 2, 'admin': False, 'createdAt': '2022-01-31T10:41:10.624Z', 'username': 'tiger', 'email': 'tiger@example.com', 'lastLoginAt': '2022-02-01T10:49:07.549Z', 'imageUrlCached': '/images/icons/user.svg', 'name': 'tiger62shin'}, 'redirectTo': None, 'grantedGroup': None, '__v': 1, 'revision': '61f7bd73834bc4803f0e2c2b'}```
このソースを読んで、動きを確認したことで、なんとなく感覚はつかめたような気がします。API である URL にパラメタを付けて GET / POST でリクエストすると結果が JSON で帰ってくるという一般的な Web API と理解しました。
当初、この CrClient をそのままか、若干の修正で使わせていただこうと思っていたのですが、
- データ移行なので更新の機能は必要ない
- 1 回だけ新規登録できれば良い。失敗したらマニュアルで削除して再実行すれば良い
- 添付ファイルが付けられない
ということで、やはり自分で作ることにしました。
必要な機能は多くはなく
- 記事の新規登録
- 記事へのファイル添付
添付したファイルの url が変わるはずなので、下記のような手順になると思う- 記事を新規登録する
- 記事にファイルを添付する
- 記事の添付ファイル参照 URL を書き換える
以下では機能ごとに調べていきますが、先にこれまで記載したサイトの他に参考にさせて頂いたのですがサイトを載せておきます。
あと、GROWI API のドキュメントのサイトにある OpenAPI specification はよく参照しました。
以下の GROWI の API 利用の例では、今回のデータ移行に必要なパラメータやレスポンスのみ記載しました。
記事の新規登録
項目 | 値 |
---|---|
URL | _api/v3/pages |
Method | POST |
Query Paramaters | access_token : API Token user : ユーザID |
Payload | body : 本文 path : 記事のパス |
Response | id : 記事のID path : 記事のパス revision : 記事のリビジョン |
import requests url = 'http://growi:3000/_api/v3/pages' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "user": "tiger"} path = "新規記事登録のサンプル" content = "この記事は GROWI API を使って登録した記事です。" payload = {"body": content, "path": "/tiger/{}".format(path)} res = requests.post(url, data=payload, params=params) pages_res = res.json() print('id : ' + pages_res['page']['id']) print('path : ' + pages_res['page']['path']) print('revision : ' + pages_res['page']['revision'])
このプログラムを実行するとコンソールに下記のように出力されます。
root@8949de830c08:/workspace# /usr/bin/env /usr/local/bin/python3.8 /root/.vscode-server/extensions/ms-python.python-2021.12.1559732655/pythonFiles/lib/python/debugpy/launcher 39727 -- /workspace/knowledge_to_growi/growiapi_sample1.py id : 61fa6161a03e4c6720a5f36a path : /tiger/新規記事登録のサンプル revision : 61fa6161a03e4c6720a5f36e
ブラウザで確認してみます。
ちゃんと登録されました。
記事へのファイル添付
項目 | 値 |
---|---|
URL | _api/attachments.add |
Method | POST |
Query Paramaters | access_token : API Token user : ユーザID |
Payload | page_id : 記事のID path : 記事のバス 記事の新規登録で戻ってきた値を指定 |
file | ファイル名, ファイル本体 |
Response | id : 添付ファイルのID originalName : ファイル名 filePathProxied : 添付ファイルのパス |
先に作成した記事に下記の画像を添付してみます。
import requests url = 'http://growi:3000/_api/attachments.add' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "user": "tiger"} file = {'file': ('setsubun_mamemaki.png', open('./knowledge_to_growi/attachments/setsubun_mamemaki.png', 'rb'), 'image/png')} payload = {'page_id': '61fa6161a03e4c6720a5f36a', 'path': '/tiger/新規記事登録のサンプル'} res = requests.post(url, data=payload, files=file, params=params) res.raise_for_status attachments_res = res.json() print('id : {} , originalName : {} , filePathProxied : {} ' .format(attachments_res['attachment']['id'], attachments_res['attachment']['originalName'], attachments_res['attachment']['filePathProxied']))
このプログラムを実行するとコンソールに下記のように出力されます。
cd /workspace ; /usr/bin/env /usr/local/bin/python3.8 /root/.vscode-server/extensions/ms-python.python-2022.0.1786462952/pythonFiles/lib/python/debugpy/launcher 40267 -- /workspace/knowledge_to_growi/growiapi_sample2.py id : 61fe69c0f6ed08f016d32137 , originalName : setsubun_mamemaki.png , filePathProxied : /attachment/61fe69c0f6ed08f016d32137 root@83fe8d69003a:/workspace#
ブラウザで確認してみます。
ちゃんと添付されました。
記事の更新
項目 | 値 |
---|---|
URL | _api/pages.update |
Method | POST |
Query Paramaters | access_token : API Token user : ユーザID |
Payload | body : 本文 pageTags : タグのリスト page_id : 記事のID (*) path : 記事のバス (*) revision_id : リビジョンID(*) (*) 記事の新規登録で戻ってきた値を指定 |
Response | revision : 記事のリビジョン (*) (*)更新するたびにリビジョンが変わるので何度も更新する場合は注意が必要 |
データ移行では本来、記事を更新する必要はないのですが、記事内の添付ファイル参照の url を書き換える必要があります。
下のソースは最初に登録した記事に添付ファイルの画像を表示するように更新するものです。
import requests url = 'http://growi:3000/_api/pages.update' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "user": "tiger"} content = 'この記事は GROWI API を使って登録した記事です。' \ '\n\n' \ '![setsubun_mamemaki.png](/attachment/61fe69c0f6ed08f016d32137)' payload = {'body': content, 'page_id': '61fa6161a03e4c6720a5f36a', 'path': '/tiger/新規記事登録のサンプル', 'revision_id': '61fa6161a03e4c6720a5f36e'} res = requests.post(url, data=payload, params=params) res.raise_for_status
ブラウザで確認してみます。
記事が更新され、画像が表示されました。
以上で、データ移行に必要な GROWI API の使い方はわかったのですが、その他にも以下のような API について使い方を調べました。
記事の一覧
項目 | 値 |
---|---|
URL | _api/pages.list |
Method | GET |
Query Paramaters | access_token : API Token user : ユーザID limit : 最大取得件数、-1 を指定すると全件 |
Payload | N/A |
Response | _id : 記事のID path : 記事のパス revision : 記事のリビジョン |
今回のデータ移行では、最初に全ての記事のリストを取得しておき、移行対象記事の存在チェックに利用しました。
import requests url = 'http://growi:3000/_api/pages.list' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 'user': 'tiger', 'limit': -1} res = requests.get(url, params=params) res.raise_for_status pages_list_res = res.json() for page in pages_list_res['pages']: print('id : {} / path : {} / revision : {} ' .format(page['_id'], page['path'], page['revision']))
このプログラムを実行するとコンソールに下記のように出力されます。
# /usr/bin/env /usr/local/bin/python3.8 /root/.vscode-server/extensions/ms-python.python-2022.0.1786462952/pythonFiles/lib/python/debugpy/launcher 41573 -- /workspace/knowledge_to_growi/growiapi_sample4.py id : 61fa6161a03e4c6720a5f36a / path : /tiger/新規記事登録のサンプル / revision : 61fa7643a03e4c6720a5f4f2 id : 61f9120a32a52003888290b5 / path : /user/tiger/自分自身のこと / revision : 61f9120a32a52003888290b9 id : 61f7bd73834bc4803f0e2c25 / path : /user/tiger / revision : 61f7bd73834bc4803f0e2c2b
添付ファイルの一覧
項目 | 値 |
---|---|
URL | _api/v3/attachment/list |
Method | GET |
Query Paramaters | access_token : API Token user : ユーザID pageid : 記事の ID (*) page : 添付ファイル一覧のページ番号 (*) 記事の新規登録 / 記事の一覧の取得で戻ってきた値を指定 |
Payload | N/A |
Response | id : 添付ファイルのID originalName : 添付ファイル名 filePathProxied : 添付ファイルの url パス |
この API は少し注意が必要です。取得できる添付ファイル情報は画面に表示される添付ファイル一覧の 1 ページ分しか取得できません。幸い page パラメーターで取得する一覧のページ番号を指定できますので、データが取得できなくなるまでページ番号をインクリメントしながら取得します。
今回のデータ移行では、すでに存在している記事に対して移行する際の添付ファイルの存在チェックに利用しました。
import requests url = 'http://growi:3000/_api/v3/attachment/list' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 'user': 'tiger', 'pageId': '61fa6161a03e4c6720a5f36a', 'page': 1} res = requests.get(url, params=params) res.raise_for_status attachment_list_res = res.json() for attachment in attachment_list_res['paginateResult']['docs']: print('id : {} , originalName : {} , filePathProxied : {} ' .format(attachment['id'], attachment['originalName'], attachment['filePathProxied']))
このプログラムを実行するとコンソールに下記のように出力されます。
# cd /workspace ; /usr/bin/env /usr/local/bin/python3.8 /root/.vscode-server/extensions/ms-python.python-2022.0.1786462952/pythonFiles/lib/python/debugpy/launcher 41445 -- /workspace/knowledge_to_growi/growiapi_sample5.py id : 61fa6799a03e4c6720a5f490 , originalName : setsubun_mamemaki.png , filePathProxied : /attachment/61fa6799a03e4c6720a5f490
添付ファイルの削除
項目 | 値 |
---|---|
URL | _api/attachments.remove |
Method | POST |
Query Paramaters | access_token : API Token user : ユーザID |
Payload | attachment_id : 添付ファイルID 記事へのファイル添付 / 添付ファイルの一覧取得で戻ってきた値を指定 |
Response | - |
今回のデータ移行では、すでに存在している添付ファイルを置き換える際の削除に利用しました。
import requests url = 'http://growi:3000/_api/attachments.remove' params = {"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 'user': 'tiger'} payload = {'attachment_id': '61fa6799a03e4c6720a5f490'} res = requests.post(url, data=payload, params=params) res.raise_for_status
このサンプルでは「記事へのファイル添付」で添付したファイルを削除しています。
添付ファイルが削除されたので表示されなくなりました。
以上です。
あと、既存の記事の取得とかあればよかったのでしょうが、今回のデータ移行には必要なかったので調べませんでした。