TripPin パート 2 - REST サービスのデータ コネクタ

このマルチパート チュートリアルでは、Power Query 用の新しいデータ ソース拡張機能の作成について説明します。 このチュートリアルは順番に実行することを目的としています。各レッスンは前のレッスンで作成したコネクタに基づいて構築され、コネクタに新しい機能が段階的に追加されます。

このレッスンの内容:

  • Web.Contents を使用して REST API を呼び出す基本関数を作成する
  • 要求ヘッダーを設定して JSON 応答を処理する方法について説明する
  • Power BI Desktop を使用して、応答をユーザー フレンドリな形式にする

このレッスンでは、(前のレッスンで作成した) TripPin サービスの OData ベースのコネクタを、任意の RESTful API に対して作成したものに似たコネクタに変換します。 OData は RESTful API ですが、固定された規則のセットを備えています。 OData の利点は、スキーマ、データ取得プロトコル、および標準クエリ言語が提供されることです。 OData.Feed を使用しない場合は、これらの機能を自分でコネクタに組み込む必要があります。

OData コネクタの要約

コネクタから OData 関数を削除する前に、サービスからデータを取得するために現在実行されているもの (ほとんどがバックグラウンド) を簡単に確認してみましょう。

Visual Studio のパート 1 から TripPin コネクタ プロジェクトを開きます。 クエリ ファイルを開き、次のクエリを貼り付けます。

TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")

Fiddler を開き、Visual Studio の [スタート] ボタンを選択します。

Fiddler では、サーバーに対する次の 3 つの要求が表示されます。

Fiddler OData requests.

  • /Me—リクエストしている実際の URL。
  • /$metadata —応答に関するスキーマと型情報を決定するために、OData.Feed 関数によって自動的に行われる呼び出し。
  • /Me/BestFriend—/Me シングルトンをリストしたときに (熱心に) 取得されたフィールドの 1 つ。 この場合、呼び出しの結果として 204 No Content 状態が発生しました。

M 評価はほとんどの場合、遅延です。 ほとんどの場合、データ値は必要なときにのみ取得またはプルされます。 値が一括でプルされるシナリオ (/Me/BestFriend のケースのように) があります。 これは、メンバーに対して型情報が必要で、エンジンには、値を取得して調べるほかに型を特定する方法がない場合に発生する傾向があります。 遅延処理される (つまり、一括プルを回避する) ことは、M コネクタのパフォーマンスを高めるための重要な側面の 1 つです。

要求と共に送信された要求ヘッダーと、/Me 要求の応答の JSON 形式に注意してください。

{
  "@odata.context": "https://services.odata.org/v4/TripPinService/$metadata#Me",
  "UserName": "aprilcline",
  "FirstName": "April",
  "LastName": "Cline",
  "MiddleName": null,
  "Gender": "Female",
  "Age": null,
  "Emails": [ "April@example.com", "April@contoso.com" ],
  "FavoriteFeature": "Feature1",
  "Features": [ ],
  "AddressInfo": [
    {
      "Address": "P.O. Box 555",
      "City": {
        "Name": "Lander",
        "CountryRegion": "United States",
        "Region": "WY"
      }
    }
  ],
  "HomeAddress": null
}

クエリの評価が完了すると、[MQuery 出力] ウィンドウに Me シングルトンのレコード値が表示されます。

OData results.

出力ウィンドウ内のフィールドと、生の JSON 応答で返されたフィールドを比較すると、不一致があることがわかります。 クエリ結果には、JSON 応答のどこにも表示されない追加のフィールド (FriendsTripsGetFriendsTrips) があります。 OData.Feed 関数では、$metadata によって返されたスキーマに基づいて、これらのフィールドがレコードに自動的に追加されます。 これは、より優れたユーザー エクスペリエンスを提供するために、コネクタがサービスからの応答を拡張または再フォーマットする方法を示す良い例です。

基本的な REST コネクタの作成

次に、Web.Contents を呼び出す新しいエクスポート関数をコネクタに追加します。

ただし、OData サービスに対して Web 要求を正常に実行できるようにするには、いくつかの標準的な OData ヘッダーを設定する必要があります。 これを行うには、コネクタで、ヘッダーの共通セットを新しい変数として定義します。

DefaultRequestHeaders = [
    #"Accept" = "application/json;odata.metadata=minimal",  // column name and values only
    #"OData-MaxVersion" = "4.0"                             // we only support v4
];

TripPin.Feed 関数の実装を変更して、OData.Feed を使用するのではなく、Web.Contents を使用して Web 要求を作成し、結果が JSON ドキュメントとして解析されるようにします。

TripPinImpl = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source)
    in
        json;

これで、クエリ ファイルを使用して Visual Studio をテストできるようになります。 /Me レコードの結果は、Fiddler 要求で確認した生の JSON に似たものになりました。

新しい関数を実行しているときに Fiddler を見ると、評価によって、3 つではなく、1 つの Web 要求が作成されていることにも注意してください。 おめでとうございます。パフォーマンスが 300% 向上しました。 もちろん、すべての型およびスキーマ情報が失われましたが、その部分に注目する必要はありません。

次のように、クエリを更新して、いくつかの TripPin エンティティまたはテーブルにアクセスします。

  • https://services.odata.org/v4/TripPinService/Airlines
  • https://services.odata.org/v4/TripPinService/Airports
  • https://services.odata.org/v4/TripPinService/Me/Trips

適切に書式設定されたテーブルを返すために使用されたパスによって、[List] が埋め込まれた状態で最上位の "value" フィールドが返されることがわかります。 Power BI シナリオで使用できるようにするには、結果に対していくつかの変換を行う必要があります。

List results.

Power Query での変換の作成

M の変換を手動で作成することは確かに可能ですが、ほとんどの人は Power Query を使用してデータを整形することを選択します。 拡張機能を Power BI Desktop で開き、それを使用してクエリをデザインし、出力をよりユーザー フレンドリな形式に変換します。 ソリューションをリビルドし、新しい拡張ファイルをカスタム データ コネクタ ディレクトリにコピーして、Power BI Desktop を再起動します。

新しい空のクエリを開始し、次を数式バーに貼り付けます。

= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")

必ず = 記号を含めてください。

元の OData フィード (AirlineCode と Name の 2 つの列を持つテーブル) のようになるまで出力を操作します。

Formatted airlines.

結果のクエリは次のようになります。

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines"),
    value = Source[value],
    toTable = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expand = Table.ExpandRecordColumn(toTable, "Column1", {"AirlineCode", "Name"}, {"AirlineCode", "Name"})
in
    expand

クエリに名前 ("Airlines") を指定します。

新しい空のクエリを作成します。 今回は、TripPin.Feed 関数を使用して、/Airports エンティティにアクセスします。 次に示す共有のようなものが表示されるまで、変換を適用します。 一致するクエリは以下にもあります。このクエリにも名前 (「Airports」) を付けます。

Formatted airports.

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airports"),
    value = Source[value],
    #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"Name", "IcaoCode", "IataCode", "Location"}, {"Name", "IcaoCode", "IataCode", "Location"}),
    #"Expanded Location" = Table.ExpandRecordColumn(#"Expanded Column1", "Location", {"Address", "Loc", "City"}, {"Address", "Loc", "City"}),
    #"Expanded City" = Table.ExpandRecordColumn(#"Expanded Location", "City", {"Name", "CountryRegion", "Region"}, {"Name.1", "CountryRegion", "Region"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded City",{{"Name.1", "City"}}),
    #"Expanded Loc" = Table.ExpandRecordColumn(#"Renamed Columns", "Loc", {"coordinates"}, {"coordinates"}),
    #"Added Custom" = Table.AddColumn(#"Expanded Loc", "Latitude", each [coordinates]{1}),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "Longitude", each [coordinates]{0}),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"coordinates"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Name", type text}, {"IcaoCode", type text}, {"IataCode", type text}, {"Address", type text}, {"City", type text}, {"CountryRegion", type text}, {"Region", type text}, {"Latitude", type number}, {"Longitude", type number}})
in
    #"Changed Type"

このプロセスは、サービスのその他のパスに対して繰り返すことができます。 準備ができたら、(モック) ナビゲーション テーブルを作成する次の手順に進みます。

ナビゲーションテーブルのシミュレーション

次に、適切に書式設定された TripPin エンティティを示すテーブル (M コードを使用) を作成します。

新しい空のクエリを開始し、詳細エディターを表示します。

次のクエリを貼り付けます。

let
    source = #table({"Name", "Data"}, {
        { "Airlines", Airlines },
        { "Airports", Airports }
    })
in
    source

プライバシー レベルの設定を [常にプライバシー レベル設定を無視します] ("高速結合" とも呼ばれます) に設定していない場合、プライバシーに関するプロンプトが表示されます。

Firewall.

複数のソースのデータを結合していて、ソースのプライバシー レベルをまだ指定していない場合、プライバシーに関するプロンプトが表示されます。 [続行] ボタンを選択し、上位のソースのプライバシー レベルを [パブリック] に設定します。

Privacy.

[保存] を選択すると、テーブルが表示されます。 これはまだナビゲーション テーブルではありませんが、後のレッスンでナビゲーション テーブルにするために必要な基本機能を提供しています。

FakeNav.

拡張機能内から複数のデータ ソースにアクセスする場合、データの組み合わせのチェックは行われません。 拡張機能内から行われるすべてのデータ ソース呼び出しでは、同じ承認コンテキストが継承されるため、結合するのは "安全" であると見なされます。 拡張機能は、データの組み合わせルールに関しては常に 1 つのデータ ソースとして扱われます。 ユーザーには、ソースを他の M ソースと結合するときに通常のプライバシー プロンプトが引き続き表示されます。

Fiddler を実行し、Power Query エディターの [プレビューを更新] ボタンをクリックすると、ナビゲーション テーブルの各項目に対して個別の Web 要求が表示されます。 これは一括評価が発生していることを示し、大量の要素を含むナビゲーション テーブルを構築する場合には最適ではありません。 後続のレッスンでは、遅延評価をサポートする適切なナビゲーション テーブルを作成する方法について説明します。

まとめ

このレッスンでは、REST サービス用の単純なコネクタを作成する方法を説明しました。 この場合、既存の OData 拡張機能を (Web.Contents を使用して) 標準の REST 拡張機能に変換しましたが、新しい拡張機能を最初から作成する場合にも同じ概念が適用されます。

次のレッスンでは Power BI Desktop を使用して、このレッスンで作成したクエリを使用し、拡張機能内の実際のナビゲーション テーブルに変換します。

次のステップ

TripPin パート 3 - ナビゲーション テーブル