TripPin 第 4 部分 - 数据源路径

本教程分为多个部分,介绍如何针对 Power Query 创建新数据源扩展。 本教程按顺序进行,每一课都建立在前几课创建的连接器的基础上,逐步为连接器添加新功能。

在本课中,你将:

  • 简化连接器的连接逻辑
  • 改进导航表体验

本课通过删除必需的函数参数,并通过移动到动态生成的导航表来改善用户体验,从而简化上一课中创建的连接器。

有关如何标识凭据的深入说明,请参阅处理身份验证“数据源路径”部分

数据源路径

调用数据源函数时,M 引擎会根据”数据源种类“和”数据源路径“值进行查找,从而标识要在评估期间使用的凭据。

上一课中,你共享了两个数据源函数,这两个函数都具有单个 Uri.Type 参数。

[DataSource.Kind="TripPin"]
shared TripPin.Feed = Value.ReplaceType(TripPinImpl, type function (url as Uri.Type) as any);

[DataSource.Kind="TripPin", Publish="TripPin.Publish"]
shared TripPin.Contents =  Value.ReplaceType(TripPinNavTable, type function (url as Uri.Type) as any);

首次运行使用其中一个函数的查询时,会收到带有下拉列表的凭据提示,用于选择路径和身份验证类型。

带有路径的凭据。

如果使用相同的参数再次运行同一查询,则 M 引擎就能找到缓存的凭据,并且不会显示任何凭据提示。 如果修改函数的参数 url,使基路径不再匹配,则会显示新路径的新凭据提示。

可以在“M 查询输出”窗口中的“凭据”表中看到任何缓存的凭据。

“凭据”选项卡。

根据更改类型,修改函数的参数可能会导致凭据错误。

简化连接器

现在,可以通过删除数据源函数 (TripPin.Contents) 的参数来简化连接器。 你还将删除 TripPin.Feed 的限定符 shared,并将其保留为仅限内部的函数。

Power Query 的设计理念之一是尽可能简化初始数据源对话框。 如果可能的话,应在导航器级别而不是连接对话框上为用户提供选择。 如果可以通过编程方式确定用户提供的值,请考虑将其添加为导航表的顶层而不是函数参数中。

例如,在连接关系数据库时,可能需要服务器、数据库和表名 知道要连接到的服务器并提供凭据后,就可以使用数据库的 API 获取数据库列表和每个数据库中包含的表列表。 在本例中,为了使初始连接对话框尽可能简单,只有服务器名称应是必需参数,DatabaseTable 则是导航表的级别。

由于 TripPin 服务具有固定的 URL 终结点,因此无需提示用户输入任何值。 你将从函数中删除 URL 参数,并在连接器中定义 BaseUrl 变量。

BaseUrl = "https://services.odata.org/v4/TripPinService/";

[DataSource.Kind="TripPin", Publish="TripPin.Publish"]
shared TripPin.Contents = () => TripPinNavTable(BaseUrl) as table;

你将保留函数 TripPin.Feed,但不再将其共享,不再将其与数据源类型相关联,并简化其声明。 从现在起,你只能在本部分文档内部使用它。

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

如果更新 TripPin.query.pq 文件中的 TripPin.Contents() 调用并在 Visual Studio Code 中运行,就会看到新的凭据提示。 请注意,现在只有一个数据源路径值 TripPin。

不带路径的凭据。

改进导航表

第一个教程中,你使用了内置 OData 函数连接到 TripPin 服务。 这样,就可以根据 TripPin 服务文档创建一个非常漂亮的导航表,而无需更多代码。 OData.Feed 函数会自动为你完成这些繁重的工作。 由于你使用的是 Web.Contents 而不是 OData.Feed进行”粗加工“,因此需要自行重新创建此导航表。

OData 导航器。

你需要进行以下更改:

  1. 定义要在导航表中显示的项目列表
  2. 请勿使用实体专用函数(GetAirlineTablesGetAirportsTable

从列表生成导航表

你将列出要在导航表中公开的实体,并生成适当的 URL 来访问它们。 由于所有实体都位于同一根路径下,因此可以动态生成这些 URL。

为了简化该示例,你将只公开三个实体集(航空公司、机场、人员),这些实体集将作为 M 中的表公开,并跳过将公开为记录的单一实例 (Me)。 我们将跳过添加函数的步骤,等到以后的课程再进行。

RootEntities = {
    "Airlines",
    "Airports",
    "People"
};

然后,更新 TripPinNavTable 函数,逐列建立表。 通过调用实体具有完整 URL 的 TripPin.Feed,检索每个实体的 [Data] 列。

TripPinNavTable = (url as text) as table =>
    let
        entitiesAsTable = Table.FromList(RootEntities, Splitter.SplitByNothing()),
        rename = Table.RenameColumns(entitiesAsTable, {{"Column1", "Name"}}),
        // Add Data as a calculated column
        withData = Table.AddColumn(rename, "Data", each TripPin.Feed(Uri.Combine(url, [Name])), Uri.Type),
        // Add ItemKind and ItemName as fixed text values
        withItemKind = Table.AddColumn(withData, "ItemKind", each "Table", type text),
        withItemName = Table.AddColumn(withItemKind, "ItemName", each "Table", type text),
        // Indicate that the node should not be expandable
        withIsLeaf = Table.AddColumn(withItemName, "IsLeaf", each true, type logical),
        // Generate the nav table
        navTable = Table.ToNavigationTable(withIsLeaf, {"Name"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
    in
        navTable;

动态生成 URL 路径时,请务必明确斜线 (/) 的位置! 请注意,Uri.Combine 在组合路径时使用以下规则:

  • relativeUri 参数以 / 开头时,它将替换参数 baseUri 的整个路径
  • 如果 relativeUri 参数以 / 开头且 baseUri 以 / 结尾,则会追加路径
  • 如果 relativeUri 参数以 / 开头且 baseUri 以 / 结尾,则替换路径的最后一段

下图显示相关示例:

Uri.Combine 示例。

删除实体特定函数

为了让连接器更易于维护,你将删除在上一课中使用的实体特定格式化函数,GetAirlineTables 以及 GetAirportsTable。 取而代之的是,你将更新 TripPin.Feed 以处理 JSON 响应,使其适用于所有实体。 具体而言,你将从返回的 OData JSON 有效负载中获取 value 字段,并将其从记录列表转换为表。

TripPin.Feed = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source),
        // The response is a JSON record - the data we want is a list of records in the "value" field
        value = json[value],
        asTable = Table.FromList(value, Splitter.SplitByNothing()),
        // expand all columns from the record
        fields = Record.FieldNames(Table.FirstValue(asTable, [Empty = null])),
        expandAll = Table.ExpandRecordColumn(asTable, "Column1", fields)
    in
        expandAll;

注意

使用通用方法来处理实体的缺点是,你失去实体的良好格式和类型信息。 本教程后面的部分将介绍如何在 REST API 调用中强制实施架构。

结束语

在本教程中,你通过修复数据源路径值并采用更灵活的导航表格式来清理和简化连接器。 完成这些步骤(或使用此目录中的示例代码)后,该 TripPin.Contents 函数将在 Power BI Desktop 中返回一个导航表。

导航器。

后续步骤

TripPin 第 5 部分 - 分页