MoreBeerMorePower

Power Platform中心だけど、ノーコード/ローコード系を書いてます。

SharePoint リストにアイテムを一括登録する ($batchの活用)

Twitterを眺めていたらPower Automateに関するツイートで「CSVをもとにしてリストを自動作成できないか」という内容がありました。

リスト作成、列追加のあたりはそれ専用のアクションが提供されていないので、SharePoint REST APIで実行する必要があります。

素朴に考えれば、N列、M行のCSVからリスト作成、アイテム登録を行う場合には Apply to eachのループを使って、 (N+M)回のAPIを実行する必要があります。

f:id:mofumofu_dance:20201018232531p:plain
ループをふんだんに使ったリスト作成とアイテム登録

しかしこの方法ではループ処理が入るので時間もかかりますし、登録するアイテム数によってはAPI実行回数が気になってきます。

そこで今回はアイテム作成部分を$batchのエンドポイントを利用することで一回で終わらせる方法を紹介します。

この方法を使うと、たとえばリストにサンプルデータを登録してテストしたいような場合に、数百件のデータが一括で登録できるという応用もできます。

$batchとは

SharePoint OnlineのREST APIを使って複数のクエリや操作をバッチ処理する方法であり、多くの操作を組み合わせた単一の要求をサーバーに送ることができます。

リクエストを送るURLは https://[domain].sharepoint.com/[site_path]/_api/$batch で、MIMEタイプは multipart/mixed でリクエストをだします。

公式のREST API関連のDocsでも記載がありますが、これだけだと少し厳しいので、この投稿では対象をアイテム登録に限って、全体としてどういうリクエストBodyの構成になるのかを紹介していきます。

docs.microsoft.com

※補足の記事として以下2つも書かれていますが、「結局何をリクエストで送ればいいのか」が読み解くのに時間がかかります。

Andrew Connell - Part 1 - SharePoint REST API Batching - Understanding Batching Requests

Andrew Connell - Part 2 - SharePoint REST API Batching - Exploring Batch Requests, Responses and Changesets

リクエストの構成

ヘッダー

Power Automateに限らず、リクエストのヘッダーには以下をセットします。

キー
Content-Type multipart/mixed;boundary=batch_e3b6819b-13c3-43bb-85b2-24b14122fed1
Expect 100-continue

この時、batch_e3b6819b-13c3-43bb-85b2-24b14122fed1部分はbatch IDと呼ばれていて、リクエストするときに guid関数batch_より後ろを作成してください。

※リクエストBody内でも利用するため、前段のステップで guid() で作成し、ヘッダーに含めるとよいです。

f:id:mofumofu_dance:20201019000035p:plain
batch IDの作成

ボディ

ここがもっとも厄介な部分です。

アイテムを2つ作成するリクエストの場合には以下のようになります。

--batch_bfd1049c-1abb-4ff3-8e78-3fea52e58d72
Content-Type: multipart/mixed; boundary="changeset_da118910-0481-46d3-a1a0-545c1def4949"
Host: https://[domain].sharepoint.com
Content-Transfer-Encoding: binary


--changeset_da118910-0481-46d3-a1a0-545c1def4949
Content-Type: application/http
Content-Transfer-Encoding: binary

POST https://[domain].sharepoint.com/[site_path]/_api/web/lists/getbytitle('リスト名')/items HTTP/1.1
Content-Type: application/json;odata=nometadata

[作成するアイテムのJSON]

--changeset_da118910-0481-46d3-a1a0-545c1def4949
Content-Type: application/http
Content-Transfer-Encoding: binary

POST https://[domain].sharepoint.com/[site_path]/_api/web/lists/getbytitle('リスト名')/items HTTP/1.1
Content-Type: application/json;odata=nometadata

[作成するアイテムのJSON]

--changeset_da118910-0481-46d3-a1a0-545c1def4949--
--batch_bfd1049c-1abb-4ff3-8e78-3fea52e58d72--

バッチには、開始と終了、そしてバッチの中身を記述するパートがあります。色分けしてみてみると下図のようになります。

f:id:mofumofu_dance:20201019003036p:plain
リクエストBodyの内訳

例えばアイテムを一度に100作る場合には、「アイテム作成1」~「アイテム作成100」まで、青い繰り返し部分が続きます。

開始パート

「バッチ開始パート」にはヘッダーで書いたbatch IDのほかに、changeset_da118910-0481-46d3-a1a0-545c1def4949 が含まれていることがわかります。これは changeset IDと呼ばれていて、アイテムを作成、変更、削除する際に1アイテムへの操作を区切る文字列として使われます。

このchangeset IDも、事前に guid() で作成しておきましょう。

繰り返し部分

対象のアイテムごとに挿入されるパートです。ここは --changeset_....の境界文字列で始まり、あとはREST APIでアイテムを作成、変更、削除する場合と同じリクエスト内容です。

[作成するアイテムのJSON] は例えばタイトル列とDescription列がある場合には {"Title":"Sample", "Description":"This is sample item"} のようになります。必須の列に値を入れるのを忘れずに!

終了パート

終了パートでは(今回は作成だけでバッチを構成しているので)、changesetの閉じとbatchの閉じ文字列を入れます。それぞれの batch ID, changeset IDをつかって

--changeset_da118910-0481-46d3-a1a0-545c1def4949--
--batch_bfd1049c-1abb-4ff3-8e78-3fea52e58d72--

この時、guidの後ろに --が入ることに注意してください。

Power Automateでの実装

フローの全体は以下の通りです。

f:id:mofumofu_dance:20201019010645p:plain
アイテム一括登録の全体

今回は適当なJSONを用意して「作成」アクションで最初に定義しています。

続いて2つの「作成」アクションでbatch IDとchangeset IDで使う guid を作っています。

比較的長い部分、これも「作成」アクションですが、アイテム作成に関する繰り返し部分の共通文字列をここで定義しています。 先ほど書いたリクエストボディでいうと

--changeset_da118910-0481-46d3-a1a0-545c1def4949
Content-Type: application/http
Content-Transfer-Encoding: binary

POST https://[domain].sharepoint.com/[site_path]/_api/web/lists/getbytitle('リスト名')/items HTTP/1.1
Content-Type: application/json;odata=nometadata

この部分です。

共通部分が定義できたら、あとは「選択」アクションを使ってアイテム登録の繰り返し部分を作ります。

f:id:mofumofu_dance:20201019011148p:plain
繰り返し部分を作るための「選択」アクション

ここでは一つだけ関数を入れています。

concat(outputs('Compose_beginning_of_the_batch_request'),decodeUriComponent('%0A'),string(item()))

やっていることは、concat関数で「共通部分」と「LFの改行コード」と「アイテムのJSON」を文字列化したもの を結合しています。

最後、「SharePointへのHTTP要求」のアクションでここまで作ってきたボディとヘッダーを使っていきます。

f:id:mofumofu_dance:20201019093239p:plain
SharePointへのHTTP要求アクション

joinの部分では以下の式を使っています。

join(body('Select'),decodeUriComponent('%0A'))

「選択」アクションの結果をLFでつなぎ合わせている操作です。

以上で用意したJSON配列から、SharePointリストへの一括登録ができました。

注意事項

一括登録も作成できるアイテム数には上限があり、正確な数字をみつけることはできませんでしたが、SharePoint 2013の時から変更されていなければ 1バッチ 100アイテム までが上限のようです。

ただ適当なリストで900件までは登録できていたので、数百のオーダーであれば適用できそうです。

それ以上を登録する場合には、Apply to eachで数回ループするようにフローを構成してください。

Software boundaries and limits for SharePoint 2013 - SharePoint Server | Microsoft Docs

f:id:mofumofu_dance:20201019012649p:plain