Box CLI では、list
コマンド(例: box users:list
など、:list
部分は省略可)を使用することでオブジェクト(ユーザーなど)の一覧を取得することができますが、上限が1000となっており、またページ割り機能も未実装のようなので(執筆時点)、1000件を超える場合は全部のデータが取得できないようです(関連Issue)。
そこで、list
コマンドを使用せずにbox request
コマンドでページ割りを行って1000件以上ある場合でも、全部のデータを取得するスクリプトを作成しました。内容を以下に記載します。
利用環境
以下の環境を使用しました
- Box CLI: ver 3.1.0(バージョン依存しないと思いますが、一応記載)
- OS: Windows 10
- Powershell: 5.1.19041.3031
MacOSおよびLinuxでも、pwsh
をインストールすることで利用できるはずです。
Ref: https://ja.developer.box.com/guides/cli/quick-start/powershell-script-templates/#前提条件
完成系
いきなり完成系から記載しちゃいます。以下の例は、Boxのゴミ箱のアイテム全件をマーカーベースのページ割りを使用して取得しCSVに出力するスクリプトになります(ゴミ箱のアイテムってすぐ多くなってウェブアプリで見るのが大変ですよね)。
# 0. Save the current encoding and switch to UTF-8. (Ref: https://stackoverflow.com/questions/58438095/powershell-string-variable-with-utf-8-encoding#answer-58438716)
$prev = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
# 1. Get 1st page via marker-based pagination
$raw_data = box request /folders/trash/items --query="""usemarker=true&limit=100""" --fields=body.entries,body.next_marker --json
# 2. Convert JSON string to JSON
$json = $raw_data | ConvertFrom-Json
# 3. Convert JSON to CSV and write it to output.csv
$json.body.entries | Export-Csv -NoTypeInformation -Path .\output.csv -Encoding utf8
$cnt = 0
while( $json.body.next_marker ) {
# 4. Build the query parameter for the next page
$next_query = "usemarker=true&limit=100&marker=" + $json.body.next_marker
# 5. Get 2nd page via marker-based pagination
$next_data = box request /folders/trash/items --query="""$next_query""" --fields=body.entries,body.next_marker --json
# 6. Convert JSON string to JSON
$json = $next_data | ConvertFrom-Json
# 7. Append the data for 2nd page to output.csv
$json.body.entries | Export-Csv -Path .\output.csv -Append -Encoding utf8
# 8. Repeat step #4 to step #7 until you reach the last page (aka next_marker is null).
$cnt++
"Page $cnt, next_marker: " + $json.body.next_marker
}
# Restore the previous encoding.
[Console]::OutputEncoding = $prev
解説
コメントをちょこちょこ入れておりますが、一応各行の処理内容について記載します。
ステップ1
- BoxCLIには
box trash
(もしくはbox trash:list
)というコマンドレットがありますが、前述の通りこちらはページ割りに対応しておらず、1000件以上のアイテムを取得することができません。 - そこで、
box request
という任意のURLやパラメータを指定できるコマンドレットを使用して、マーカーベースのページ割りのパラメータであるusemarker=true
をCLIへと渡します。 - なお、
box request
を使用すると、以下のようにステータスコードやレスポンスヘッダーなど不要な情報も含まれます。
PS C:\Users\kojimaru> box request /folders/trash/items --query="""usemarker=true&limit=100"""
Status Code: 200
Headers:
Date: 'Sun, 17 Sep 2023 22:47:32 GMT'
Content-type: application/json
Transfer-encoding: chunked
X-envoy-upstream-service-time: '364'
Box-request-id: 185e9224815c2641f06cad53eeb33f71
Cache-control: 'no-cache, no-store'
Strict-transport-security: max-age=31536000
Via: 1.1 google
Alt-svc: 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000'
Body:
Entries:
-
Type: folder
ID: '85557482328'
Sequence ID: '1'
ETag: '1'
Name: '26061'
Limit: 1
Next Marker: >-
eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNORGd5TXpJNGZRIn0
ので、--fields=body.entries,body.next_marker
で必要なフィールドのみが出力されるように指定します。また、--json
でJSON形式になるように指定します
PS C:\Users\kojimaru> box request /folders/trash/items --query="""usemarker=true&limit=100""" --fields=body.entries,body.next_marker --json
{
"body": {
"entries": [
{
"type": "folder",
"id": "85557482328",
"sequence_id": "1",
"etag": "1",
"name": "26061"
}
],
"next_marker": "eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNORGd5TXpJNGZRIn0"
}
}
※ちなみに
ステップ2
- ステップ1で取得したデータはJSON形式に成形されてますが、データ型としては文字列なので
ConvertFrom-Json
でJSONオブジェクトに変換します。
ステップ3
- JSONオブジェクトの要素である
body.entries
の内容(最初のページの100件)をExport-Csv
でCSVファイルへと書き出します。 -Encoding utf8
を指定することでBOM付きのCSVとして出力します(Excelで開く際の文字化け防止)。-NoTypeInformation
を指定することでカラムヘッダーの上の行に挿入される不要な行(#TYPE情報ヘッダー)を削除します。
ステップ4
body.next_marker
が存在する場合(次のページが存在する場合)は、そのmarkerを使用して次のページを取得するためのクエリ文字列を生成します。
ステップ5~8
- ステップ4のクエリ文字列を使用してリクエストを送信します。
- 結果をJSONオブジェクトに変換し(ステップ2と同様)、それをCSVファイルに追記(
-Append
)します body.next_marker
が存在する場合(さらに次のページが存在する場合)は、そのmarkerを使用して次のページを取得するため、ステップ4~7を繰り返します。body.next_marker
が存在しない場合はこれ以上のアイテムがない(最終ページに到達した)ということなので、ループから抜けて終了します。
ステップ0
- ここまでの解説ではスルーしてましたが、この前処理は一体なんなのか?これは文字化け防止の処理になります。
- 今回のスクリプトでは、Box CLI(外部プログラム)で取得した出力結果を変数に代入しているのですが、その際に
[Console]::OutputEncoding
で定義されたエンコード方式を使用してしまうようです。よって、今回の私の日本語OSのような環境だと、以下のようにshift-jisが使用されることでBox CLIの出力エンコード方式と一致せず、文字化けが起きているようでした。
PS C:\Users\kojimaru> [Console]::OutputEncoding
BodyName : iso-2022-jp
EncodingName : 日本語 (シフト JIS)
HeaderName : iso-2022-jp
WebName : shift_jis
WindowsCodePage : 932
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
IsSingleByte : False
EncoderFallback : System.Text.InternalEncoderBestFitFallback
DecoderFallback : System.Text.InternalDecoderBestFitFallback
IsReadOnly : True
CodePage : 932
- よって、UTF-8を使用するように
[Console]::OutputEncoding]
に[System.Text.UTF8Encoding]::new()
をセットすることで回避できました。 - ちなみに、最初の行で
$prev
に既存の出力エンコード方式を保存し、最終行で元の($prev
の)設定に戻してます。 - 参考記事: https://stackoverflow.com/questions/58438095/powershell-string-variable-with-utf-8-encoding#answer-58438716
実行
- スクリプトの内容をコピーして「GetTrashedFolders.ps1」というファイル名に保存して、Powershellから実行してみます。
- すると以下のようにコンソール上にページ遷移の進捗が記録され、「output.csv:にもりもり各ページのアイテムが追記されていきます。
PS C:\Development\BoxCLI> .\GetTrashedFolders.ps1
Page 1, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOVFE0TXpJNGZRIn0
Page 2, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOVGcxTVRNeWZRIn0
Page 3, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOakU0T1RZM2ZRIn0
Page 4, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOalV4TlRJNGZRIn0
Page 5, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOamcxT1RNeWZRIn0
Page 6, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOekV5TnpJNGZRIn0
Page 7, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOelF3TXpJNGZRIn0
Page 8, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOelkyTnpJNGZRIn0
Page 9, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNOemt5TnpNeWZRIn0
Page 10, next_marker: eyJ0eXBlIjoiT3duZWRCeUZvbGRlciIsImRpciI6Im5leHQiLCJ0YWlsIjoiZXlKMmFXVjNVMk52Y0dVaU9pSmhiR3dpTENKc1lYTjBTV1FpT2pnMU5UVTNPREUzT1RNeWZRIn0
<snip>
おわりに
- 今回の例ではBox APIで2種類あるページ割りのうち、マーカーベースのページ割りを使用しましたが(こっちのほうがめんどくさそうだったので敢えて)、もう片方のオフセットベースのページ割りも
box request
でパラメータとしてoffset
の値を指定しインクリメントしながらページ遷移することで同様のことができると思います。 - また、ゴミ箱のアイテム取得を例にしましたが、他のリソース(ユーザーやコラボレーションなど)でも同様の処理が可能です。
- 公式でもCLIを使用したPowershellのスクリプトをいくつか用意しているようなので、そちらも見てみるとおもしろいかも https://ja.developer.box.com/guides/cli/scripts/#powershellスクリプト