mcpField
D
dataflows-authoring-cli

dataflows-authoring-cli

github.com/microsoft数据与数据库1.3k107 次安装3 周前更新
代码仓库

关于 dataflows-authoring-cli

面向 Microsoft Fabric Dataflows Gen2 编写的命令行技能,指导 Agent 创建、修改、预览和验证数据流,并可选择刷新。变更前要求先明确需求。

主要功能

  1. 01端到端创建数据流
  2. 02修改现有数据流
  3. 03预览候选 M 查询
  4. 04管理输出目标
  5. 05验证定义与连接绑定
  6. 06按需刷新数据流

适用场景

  1. 01按指定来源和转换创建 Dataflow
  2. 02更新现有数据流定义
  3. 03保存更改前预览查询结果
  4. 04将查询结果写入支持的目标

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/microsoft/skills-for-fabric --skill dataflows-authoring-cli

安装后在 Claude Code 中输入/dataflows-authoring-cli确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
authoring-cli-quickref.md
authoring-script-templates.md
connection-management.md
connectors.md
m-language.md
mashup-preview.md
output-destinations.md
references/charts/
bar_chart.py
line_chart.py

详细介绍

更新检查 — 每个会话仅执行一次(强制) 首次在会话中使用此技能时,继续操作前先运行 check-updates 技能。

  • GitHub Copilot CLI / VS Code:调用 check-updates 技能。
  • Claude Code / Cowork / Cursor / Windsurf / Codex:比较本地与远程的 package.json 版本。
  • 如果本会话中已执行过检查,则跳过。

重要注意事项

  1. 若要根据工作区名称查找工作区详情(包括其 ID):列出所有工作区,然后使用 JMESPath 进行筛选。
  2. 若要根据工作区 ID、项目类型和项目名称查找项目详情(包括其 ID):列出该工作区中该类型的所有项目,然后使用 JMESPath 进行筛选。

变更前置条件检查(强制) 进行任何变更之前,确认用户对所请求操作的意图足够明确。 对于创建或更新请求,需明确源、查询/架构和转换方式、目标行为以及刷新行为(包括明确选择不设目标或不刷新)。如果请求较为笼统,例如“为报表设置我需要的 Dataflow”,则应提出结构化的澄清问题,并在变更前停止。不要根据无关的工作区项目推断需求,也不要创建基于猜测的源、连接或 Dataflow。

dataflows-authoring-cli — 通过命令行创作 Dataflows Gen2

目录

此技能(SKILL.md)

章节说明
工具栈az + jq + base64 + curl
连接发现工作区/数据流 ID
Agent 工作流从这里开始。 A:端到端创建;B:修改现有内容;C:预览循环
必须做 / 避免 / 优先选择编写规则
故障排除症状 → 修复对照表
示例可运行的 bash + PowerShell 示例
输出要求响应规范

参考资料(位于 references/)

文件阅读时机
authoring-cli-quickref.md单行命令示例、状态枚举、base64 辅助工具、连接绑定快速模式
authoring-script-templates.md完整的 bash + PowerShell 模板;端到端冒烟测试;LRO 轮询模式
connection-management.md列出/创建/检查连接;supportedConnectionTypes;解析 ClusterId;ID 格式速查表
connectors.mdM 端源连接器:经实时验证的函数清单、Lakehouse 深度导航、Web.Page / Web.BrowserContents 的网关范围,以及 Html.Table / Csv.Document / Json.Document 模式
m-language.mdDataflow Gen2 的 M 语言语义:try 记录结构、列转换中的单元格级错误包装、行与子表上下文中的 each 作用域、可选字段访问 [?] / Record.FieldOrDefault、带引号的标识符、沙盒禁用的符号(File.Contents)
mashup-preview.mdexecuteQuery 契约:引导分支、自动包装规则、严格避免无界预览
output-destinations.md输出目标模式:Lakehouse Table、Lakehouse Files、Warehouse、ADX、Azure SQL。DataDestinations 注释、隐藏查询、loadEnabled 规则、连接限制

常用参考资料(位于 ../../common/)

文件阅读时机
COMMON-CLI.mdaz login、令牌获取、az rest、分页、LRO 轮询、CLI 注意事项。§ 在 Fabric 中查找工作区和项目为必读内容。
COMMON-CORE.mdFabric 拓扑、环境 URL、身份验证、核心 REST API 范围
ITEM-DEFINITIONS-CORE.md定义信封;各项目类型的负载契约
DATAFLOWS-AUTHORING-CORE.md编写能力矩阵;三部分定义结构;M 结构;连接模型;ALM / Git 集成

相关技能

技能用途
dataflows-consumption-cli执行已持久化查询;在无意持久化的情况下,通过 customMashupDocument 进行临时只读查询;将 Arrow 转换为 CSV/pandas;刷新状态/历史记录。

工具栈

工具作用安装
az CLI主要工具:身份验证(az login)、REST API 调用(az rest)、获取令牌。大多数开发环境中已预装
jq解析和处理 JSON 响应及定义负载。已预装或安装简单
base64对 REST API 的定义部分进行编码/解码。bash 内置;PowerShell 中可使用 [Convert]::ToBase64String()
curl需要直接控制原始 HTTP 时,作为 az rest 的替代方案。已预装
uuidgen创建新数据流定义时,为 queryId 和 logicalId 生成每个查询/平台的 GUID(工作流 A)。Linux/macOS 已预装;Windows 上使用 PowerShell [guid]::NewGuid().Guid 或通过 WSL 运行

Agent 检查 — 首次操作前,请确认 az、jq 和 curl 均可用。uuidgen 仅在工作流 A(创建)中需要。 安装和身份验证设置请参见 COMMON-CLI.md。


连接

发现工作区和数据流 ID

根据 COMMON-CLI.md 中的 Fabric“查找工作区和项目”:

# List workspaces — find workspace ID by name
az rest --method get \
  --resource "https://api.fabric.microsoft.com" \
  --url "https://api.fabric.microsoft.com/v1/workspaces" \
  --query "value[?displayName=='MyWorkspace'].id" --output tsv

# List dataflows in workspace — find dataflow ID by name
WS_ID="<workspaceId>"
az rest --method get \
  --resource "https://api.fabric.microsoft.com" \
  --url "https://api.fabric.microsoft.com/v1/workspaces/$WS_ID/dataflows" \
  --query "value[?displayName=='MyDataflow'].id" --output tsv
可复用的连接变量
WS_ID="<workspaceId>"
DF_ID="<dataflowId>"
API="https://api.fabric.microsoft.com/v1"
RESOURCE="https://api.fabric.microsoft.com"

Agent 工作流

以下三个工作流涵盖常见的创作任务:

A. 端到端创建新数据流

当数据流尚不存在时使用此工作流。涵盖完整的顺利流程:发现或创建连接、创建数据流框架、在一次 updateDefinition 中保存 M 和绑定、验证,并可选择刷新。

步骤:

  1. 列出现有连接,并按 connectionDetails.type 和目标 URL/主机进行筛选——如果存在匹配项,则重复使用(GET /v1/connections + JMESPath)。
  2. 如果没有匹配项,则创建连接。 首先调用 GET /v1/connections/supportedConnectionTypes,以发现必需参数和支持的凭据类型,然后调用 POST /v1/connections(同步返回 201)。请求体结构和凭据架构:connection-management.md。
  3. 解析复合绑定所需的 ClusterId。 调用 GET https://api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources,并使用 --query "value[?id=='$CONN_ID'] | [0].clusterId",受众为 --resource "https://analysis.windows.net/powerbi/api"(末尾不带斜杠)。按 ID 查询的路由会对云连接返回 PowerBIEntityNotFound。新创建的连接可能需要几秒钟才会出现——结果为空时请重试。详情:connection-management.md § Resolving ClusterId。
  4. 创建数据流外壳。 调用 POST /v1/workspaces/{ws}/dataflows 并传入 {"displayName":"<displayName>"},同步返回 201。创建时 definition 字段为可选项,可在下一步设置。如果改为在此创建 POST 中提供完整的 definition(包含全部三个部分),则此调用就是持久化入口——在摘要中将 POST /v1/workspaces/{ws}/dataflows(而非 updateDefinition)列为持久化路径。
  5. 一次调用保存 M 和连接绑定。 调用 POST /v1/workspaces/{ws}/dataflows/{df}/updateDefinition?updateMetadata=true 并传入三个部分:mashup.pq(实际的 Web.Contents / Sql.Database / …)、queryMetadata.json(其中填充了 connections[];每个 connectionId 都是复合 {"ClusterId":"…","DatasourceId":"…"} 的字符串化形式),以及 .platform。通常同步返回 200;请求体较大时可能返回 202 和 LRO Location——两种情况都要处理。
  6. 验证绑定已持久化。 再次调用 getDefinition,解码 queryMetadata.json,并确认 connections[] 保持完整。验证时不要使用 GET /items/{id}/connections——该端点反映的是刷新后物化的状态,而非已持久化的定义,即使绑定成功后也会返回 0。参见 AVOID。 仅用于预览的受限回退方案: 如果 Power BI v2 gatewayClusterDatasources 返回 401/403/Unauthorized,或重试后仍看不到 ClusterId,导致无法继续绑定连接,请勿在创建数据流之前停止。创建外壳,保存查询定义但不要声称源绑定有效;对每个已保存的查询仅使用 QueryName 调用 executeQuery,从 Arrow 流中显示确切的凭据或绑定错误,不要编造图表数据,也不要刷新。
  7. (建议)提供 ASCII 图表预览。 询问用户:"Would you like me to preview the data as charts before the first refresh?"。在此创建流程中,定义已在步骤 5 保存,因此此处的图表预览是通过刷新物化数据之前的保存后验证关卡,而非保存前的步骤。(如果要在首次 updateDefinition 之前验证候选 M——例如迭代 M,或为带凭据的源进行引导式绑定,以便 executeQuery 能访问它——请使用保存前的 Preview-Driven Authoring Loop;图表渲染方式相同,只有相对于保存的顺序不同。)如果用户接受,则对每个实体调用 executeQuery,解析 Arrow IPC 流,通过 references/charts/line_chart.py / references/charts/bar_chart.py 渲染折线图(时间序列)或水平条形图(类别),并在继续之前请用户确认。详情:mashup-preview.md § ASCII chart preview。如果用户拒绝,则直接继续步骤 8。
  8. (可选)触发刷新以物化数据。调用 POST .../jobs/instances?jobType=Refresh,请求体为 {"executionData":{"executeOption":"ApplyChangesIfNeeded"}}。任何定义变更后的首次刷新都必须使用 ApplyChangesIfNeeded——否则 Fabric 会刷新此前已应用的定义。轮询 LRO,直到 status 为 Completed(刷新枚举值)、Failed 或 Cancelled。
# Concise skeleton — full runnable bash is Example 1 below.
# PowerShell + LRO-polled variants: references/authoring-script-templates.md

WS_ID="<workspaceId>"; URL="<source-url>"
RES="https://api.fabric.microsoft.com"; API="$RES/v1"
PBI="https://analysis.windows.net/powerbi/api"

# 1. List existing & try reuse
CONN_ID=$(az rest --method get --resource "$RES" --url "$API/connections" \
  --query "value[?connectionDetails.type=='Web' && connectionDetails.path=='$URL'] | [0].id" -o tsv)

# 2. Create connection if missing — see connection-management.md for full body
# 3. List+filter for ClusterId
CLUSTER_ID=$(az rest --method get --resource "$PBI" \
  --url "https://api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources" \
  --query "value[?id=='$CONN_ID'] | [0].clusterId" -o tsv)

# 4. Empty dataflow shell — sync 201
SHELL_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"displayName":"my-df"}' > "$SHELL_BODY"
DF_ID=$(az rest --method post --resource "$RES" \
  --url "$API/workspaces/$WS_ID/dataflows" \
  --headers "Content-Type=application/json" \
  --body "@$SHELL_BODY" --query id -o tsv)
rm -f "$SHELL_BODY"

# 5. One-shot updateDefinition with real M + connections[] (sync 200 typical)
#    Body assembly (mashup.pq + queryMetadata.json + .platform, base64-encoded;
#    queryMetadata.json.connections[].connectionId = composite ClusterId/DatasourceId):
#    see Example 1 below.

# 6. Verify via getDefinition (NOT GET /items/{id}/connections — see AVOID)
# 7. (optional) executeQuery — Workflow C
# 8. (optional) Refresh with executeOption=ApplyChangesIfNeeded — Example 2

一次性调用与两步绑定+保存。 第 4–5 步可以合并为一次调用(默认方式,可节省一次 HTTP 往返),也可以拆分为先执行引导绑定的 updateDefinition,再执行完整的 M updateDefinition。两种方式都可行——请参阅 PREFER。

B. 修改现有数据流

当数据流已存在时使用此流程。标准的“发现 → 制定 → 执行 → 验证”循环。如果数据流尚不存在,请改用 工作流 A。

  1. 发现 — 列出工作区、列出数据流,并调用 getDefinition(解码 mashup.pq 和 queryMetadata.json)。通过 GET /v1/connections/{id} 验证所有 connections[] 条目。
  2. 制定 — 修改 M、重新编码各部分,并确保调用方的连接存储中存在每个被引用的 connectionId。
  3. 执行 — 使用全部 3 个部分(完整替换)调用 POST .../updateDefinition?updateMetadata=true。可选择触发刷新。
  4. 验证 — 再次调用 getDefinition 以确认更改;轮询刷新 LRO;对于刷新失败,最多尝试一次 executeQuery 隔离检查,以定位可修复的 M/源问题。对于终止性/不可重试的失败(isRetriable: false、工作区范围的 UnknownException),呈现原始错误并停止,不要再次触发。
# Concise skeleton — full templates: references/authoring-script-templates.md
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = $RESOURCE).
RESOURCE="https://api.fabric.microsoft.com"; API="$RESOURCE/v1"

# 1. Discover — getDefinition (handles 200 sync and 202 + LRO via curl)
HDR=$(mktemp); BODY=$(mktemp)
CODE=$(curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Length: 0" \
  "$API/workspaces/$WS_ID/dataflows/$DF_ID/getDefinition" \
  -D "$HDR" -o "$BODY" -w "%{http_code}")
if [ "$CODE" = "202" ]; then
  LOC=$(tr -d '\r' < "$HDR" | grep -i "^location:" | awk '{print $2}')
  RETRY=$(tr -d '\r' < "$HDR" | grep -i "^retry-after:" | awk '{print $2}'); RETRY=${RETRY:-5}
  while :; do
    sleep "$RETRY"
    OP=$(az rest --method get --resource "$RESOURCE" --url "$LOC")
    case "$(echo "$OP" | jq -r '.status // empty')" in
      Succeeded) RESULT=$(az rest --method get --resource "$RESOURCE" --url "${LOC%/}/result"); break ;;
      Failed|Cancelled) echo "ERROR: getDefinition $(echo "$OP" | jq -r '.status')" >&2; exit 1 ;;
    esac
  done
else
  RESULT=$(cat "$BODY")
fi
rm -f "$HDR" "$BODY"

# Validate bound connections (connectionId is a composite JSON string — iterate safely)
QUERY_META=$(echo "$RESULT" | jq -r '.definition.parts[] | select(.path=="queryMetadata.json") | .payload' | base64 -d)
echo "$QUERY_META" | jq -c '.connections[]?' | while IFS= read -r conn; do
  RAW=$(echo "$conn" | jq -r '.connectionId')
  DATASOURCE_ID=$(echo "$RAW" | jq -r '.DatasourceId? // empty' 2>/dev/null)
  [ -z "$DATASOURCE_ID" ] && DATASOURCE_ID="$RAW"
  # GET /v1/connections/$DATASOURCE_ID to confirm access
done

# 2-3. Formulate & Execute — see Example 3
# 4. Verify — trigger refresh via curl (az rest cannot capture Location header).
#    Full LRO polling: references/authoring-script-templates.md.
C. 预览驱动的编写循环(保存前执行 executeQuery — 请参阅 mashup-preview.md)

当更改涉及 Power Query M(新查询、编辑 mashup、新数据源、更改参数)时,应在持久化之前,针对数据流绑定的连接预览候选 customMashupDocument。这样可以在编写时捕获语法、架构和凭据错误。完整的有序步骤、引导分支、自动包装规则、对无界预览的严格规避要求、ASCII 图表预览和 Apache Arrow 处理方式,请参阅:mashup-preview.md § 预览驱动的编写循环。

意图区分。 此工作流用于保存前的意图。若要执行已保存的查询(仅使用 QueryName),或运行不打算持久化的临时只读 customMashupDocument,请使用 dataflows-consumption-cli。mashup-preview.md 是适用于这两种意图的共享 API 参考。

仅在只修改元数据(显示名称、计划、loadEnabled 开关),或 Agent 记录了明确的跳过原因(引导、成本过高、有副作用的数据源)时,才跳过预览。

D. 输出目标

当数据流需要将查询结果写入外部存储(Lakehouse 表、Lakehouse 文件、Warehouse、ADX、Azure SQL)时使用此方式。此方式基于工作流 A 扩展,增加 DataDestinations 注释和隐藏的目标查询。完整参考和示例请参阅:output-destinations.md。

关键要求:

  1. 源查询必须包含 [DataDestinations = {[...]}] 注释,并通过名称引用目标查询。
  2. 隐藏的目标查询(后缀为 _DataDestination)使用空值安全运算符 ?[Data]?(表)或 ?[Content]?(文件)导航到目标存储。
  3. queryMetadata.json 必须在目标查询上设置 "loadEnabled": false,否则刷新会失败。在摘要中使用字面部分名称说明这一点(例如:“在 queryMetadata.json 的目标查询上设置 loadEnabled: false”)。
  4. 始终对通过 API 创建的数据流使用 IsNewTarget = true,即使目标表已存在。
  5. 绑定相应的连接(Lakehouse:种类为 "Lakehouse";Warehouse:种类为 "Warehouse";ADX:种类为 "AzureDataExplorer";Azure SQL:种类为 "Sql"),并使用组合的 ClusterId/DatasourceId ID。
  6. 首次刷新必须使用 ApplyChangesIfNeeded 来发布草稿并协调注释。
  7. 所有源列都必须指定类型——所有目标类型都不接受 Any 类型的列。
  8. **在书面摘要中注明定义部分的名称。**由于 CLI 会话记录会截断较长的命令正文,最终摘要(正文,而不只是 shell 命令)必须使用其字面路径注明三个定义部分——mashup.pq、queryMetadata.json 和 .platform,以确保答案中保留部分名称(例如:“通过 updateDefinition 保存了 mashup.pq + queryMetadata.json + .platform”)。不要将 queryMetadata.json 缩写为“query metadata”,也不要将内部字段 queriesMetadata 用作替代。

支持的目标:

目标连接种类目标查询函数备注
Lakehouse 表LakehouseLakehouse.Contents(...)路径:"Lakehouse"
Lakehouse 文件LakehouseLakehouse.Contents(...)TypeSettings = [Kind = "File"]、?[Content]?
WarehouseWarehouseFabric.Warehouse(...)路径:"Warehouse",Schema/Item 导航
Azure Data ExplorerAzureDataExplorerAzureDataExplorer.Contents(...)路径必须与连接完全一致(包括末尾斜杠!)
Azure SQLSqlSql.Database(...)路径:"server;database"

**最简步骤:**创建数据流 → 查找/创建连接 → 解析 ClusterId → 保存带有 OD 注释的定义 → 验证 → 刷新。

# Skeleton — full PowerShell recipe: references/output-destinations.md § Complete Example
WS_ID="<workspaceId>"; LH_ID="<lakehouseId>"; RES="https://api.fabric.microsoft.com"

# M pattern (two queries):
# 1. Source with [DataDestinations] annotation
# 2. Hidden _DataDestination query with ?[Data]? null-safe navigation
# queryMetadata: source loadEnabled=true, destination loadEnabled=false + isHidden=true
# Refresh: {"executionData":{"executeOption":"ApplyChangesIfNeeded"}}

注意事项、规则与故障排除

完整的编写注意事项请参阅:DATAFLOWS-AUTHORING-CORE.md 中的“注意事项和故障排除”。 CLI 专属问题请参阅:COMMON-CLI.md 中的“注意事项与故障排除(CLI 专属)”。 连接发现请参阅:authoring-cli-quickref.md § Connection Discovery and Validation。

必须执行
  • 在新数据流首次刷新前,主动询问是否预览每个实体——创建框架并通过 updateDefinition 绑定连接(这会保存定义)后,在通过刷新实现数据流之前,询问用户是否想查看预览图表。在预览驱动的循环中,预览则应先于保存定义的 updateDefinition。如果用户接受,请遵循 mashup-preview.md § ASCII 图表预览。仅元数据编辑(显示名称、计划)或 Agent 记录了明确的跳过原因时,才跳过。
  • 先执行 az login——所有 az rest 调用都使用当前活动会话。没有会话 → 401。
  • Fabric API 使用 --resource "https://api.fabric.microsoft.com"。 对于 Power BI v2(gatewayClusterDatasources),使用 --resource "https://analysis.windows.net/powerbi/api",末尾不要带斜杠——带斜杠的形式会导致 AADSTS500011 invalid_resource。
  • 对全部 3 个定义部分进行 Base64 编码——mashup.pq、queryMetadata.json 和 .platform,每个部分的 payloadType 都设为 "InlineBase64"。updateDefinition 会完整替换定义;只发送 1 或 2 个部分会悄悄丢弃查询。
  • 同时处理同步和异步响应。 POST /dataflows、updateDefinition 和 getDefinition 通常返回同步响应(200/201),但在请求体较大时也可能返回 202 和 LRO Location——两种情况都要处理。参见 authoring-script-templates.md § Fabric LRO 轮询模式。
  • 在 queryMetadata.json 中设置 formatVersion: "202502",并包含一个与 displayName 匹配的顶层 name——省略任一项都会导致保存时失败或显示名称状态过时。
  • loadEnabled 是选择退出,而非选择加入。 Fabric 默认会将每个查询自动加载到暂存 Lakehouse;仅对不希望写入的辅助查询设置 loadEnabled: false。注意:通过 getDefinition 往返读取时,loadEnabled: true 也会从 queryMetadata.json 中剥离(这是默认值)——回读时没有该字段并非错误。详情:DATAFLOWS-AUTHORING-CORE.md § loadEnabled 语义。
  • 根据上下文使用正确的 ID 格式。 REST /v1/connections 操作使用 connection.id 中的纯 GUID;queryMetadata.json connections[].connectionId 使用字符串化的组合 ID {"ClusterId":"…","DatasourceId":"…"}。参见 connection-management.md § 连接 ID 格式速查表。
  • 通过列表查询加筛选来解析 ClusterId。 使用 GET .../gatewayClusterDatasources,并按 value[?id=='$CONN_ID'] 筛选。按 ID 查询的路由对云连接会返回 PowerBIEntityNotFound;新建的连接可能需要等待 5-15 s 后重试。参见 connection-management.md § 解析 ClusterId。
  • executeQuery 请求体使用顶层 QueryName 字段(规范形式为 PascalCase;线上字段名不区分大小写,因此小写的 queryName 也能正常处理)。其值必须是持久化 M 或所提供的 customMashupDocument 中的一个 shared 成员。{"queries":[…]} 数组结构始终会失败,并返回 DataflowExecuteQueryError: Invalid query name;查询名称错误则返回 QueryNotFound。完整契约:mashup-preview.md § 请求体。
  • 使用准确且区分大小写的 API 名称。 端点是 executeQuery(单数,绝不能写成 executeQueries),请求体字段是 customMashupDocument(绝不能写成 mashupDocument,也不能进行 Base64 编码——它是纯 UTF-8 M 字符串)。同一 M 请求体会成为保存的 mashup.pq 部分,并通过 customMashupDocument 引用。术语表:mashup-preview.md § 术语。
  • 任何 updateDefinition 之后的首次刷新都必须使用 executeOption: "ApplyChangesIfNeeded"。 请求体:{"executionData":{"executeOption":"ApplyChangesIfNeeded"}}。否则,Fabric 会刷新先前已应用的定义。
  • 将终态刷新失败视为停止条件——不要进入调试循环。 刷新/LRO 作业达到终态 Failed/Cancelled,或后端错误包含 isRetriable: false(或工作区范围的 UnknownException)时,逐字报告原始错误并停止。这些是 Agent 无法通过重试修复的后端/基础设施结果——不要重新触发刷新、继续轮询或展开长时间调查。最多进行一次 executeQuery 隔离尝试,以定位可修复的 M/源问题;如果未发现定义侧问题,则结束并报告错误。
  • 在 POST /v1/connections 之前调用 GET /v1/connections/supportedConnectionTypes——绝不要猜测参数名称或凭据类型;它们会因连接器、租户和时间而异。向用户总结连接器的必需参数或 credentialType 集合时,使用大小写完全准确的端点路径 GET /v1/connections/supportedConnectionTypes。纯查询也适用(“连接器 X 支持哪些参数/credentialType?”):对租户执行实时的 GET /v1/connections/supportedConnectionTypes。随附的 connection-management.md 参考文档介绍了响应结构,但不能替代租户特定且区分大小写的值,因为这些值会因连接器和时间而异。
  • 刷新前验证所引用的连接。 对 queryMetadata.json 中的每个 connectionId,执行 GET /v1/connections/{id}(从组合 ID 中提取纯 GUID)。刷新时出现难以理解的 EntityUserFailure,通常是因为连接缺失或无法访问。参见 connection-management.md。
  • 预览带凭据的 M 之前,先建立并绑定连接。 初始创建请求中的 connections[] 数组尚不能被 executeQuery 使用;至少先通过一次 updateDefinition 将其保存。详情:mashup-preview.md § 引导分支。
  • 在 customMashupDocument 中发送完整的 section Section1; ... 文档——executeQuery 不会自动封装原始表达式。参见 mashup-preview.md § customMashupDocument 格式。
  • 在 updateDefinition 之前,通过 executeQuery 预览候选 M——元数据编辑除外,或 Agent 记录了明确的跳过原因时除外。将预览成功视为“M 可执行”;将后续刷新视为真正的是否继续判断。
  • 通过 --body "@<file>" 传递 JSON 请求体,不要内联传递。 写入 $env:TEMP\<name>.json(PowerShell 使用 UTF-8 无 BOM,通过 [IO.File]::WriteAllText)或 /tmp/<name>.json(bash)。在 bash 中内联使用 --body "<json>" 很脆弱;在 Windows 上则会出错,因为 cmd.exe 的参数解析器会破坏嵌入的引号。参见 authoring-script-templates.md § PowerShell — 使用定义创建数据流。
  • 无人值守刷新时,优先使用 WorkspaceIdentity / ServicePrincipal 凭据。 OAuth2 搭配 singleSignOnType: None 可用于交互式 executeQuery,但在租户条件访问策略下用于服务上下文刷新时不稳定。通过 supportedConnectionTypes 检查支持的类型。
避免
  • 在未向用户提供预览的情况下创建新的数据流(首次刷新) — 仅阅读代码,用户无法验证 M 代码是否符合其意图。首次刷新前,始终提供以 ASCII 图表预览每个实体输出的选项(在预览驱动的循环中,也要在持久化 updateDefinition 之前提供)。用户可以拒绝,但始终应提供此选项。
  • 向 definition 添加 format 属性 — Items API 仅使用 parts[];"format": "json" 会返回 400 InvalidDefinitionFormat。
  • 硬编码工作区/数据流 GUID — 通过 REST API 发现(参见 Connection 部分)。
  • 使用 GET /v1/workspaces/{ws}/items/{itemId}/connections 验证刚绑定的数据流。 它反映的是刷新后物化的状态,而非持久化的定义,绑定成功后会返回 0。通过 getDefinition 并解码 queryMetadata.json.connections[] 进行验证。
  • 假设 updateDefinition / POST /dataflows 始终是 LRO。 常见响应是同步的(200/201);应处理这两种响应格式 — 参见上方的必须执行事项。
  • 请求带有尾部斜杠的 PBI v2 令牌(--resource "https://analysis.windows.net/powerbi/api/")— 会失败并返回 AADSTS500011 invalid_resource。使用不带斜杠的形式。
  • 对云连接使用 gatewayClusterDatasources/{id} 逐个查询 — 会返回 PowerBIEntityNotFound。使用列表并筛选(参见上方的必须执行事项)。
  • 为 executeQuery 使用 {"queries":[…]} 数组请求体格式 — 无论内部字段采用何种大小写,都会返回 400 DataflowExecuteQueryError: Invalid query name。使用顶层 QueryName(或 queryName — 字段不区分大小写);每次调用只选择一个查询。
  • 使用 GET 调用 getDefinition — 它是 POST 端点;GET 会返回 405。
  • 手动构造操作 URL — 始终遵循 202 响应中的 Location 标头。
  • 重复使用 displayName 值 — 虽然不会强制禁止,但会造成混淆。
  • 按显示名称绑定连接 — 连接 ID 才是权威依据;名称可能会更改。
  • 假设所有用户都能访问所有连接。 可见性因调用者而异:无权访问的调用者调用 GET /v1/connections/{id} 可能会收到 403/404。GET /v1/connections 返回空结果并不能证明连接不存在。
  • 手动编写缺少 supportedConnectionTypes 的连接请求体 — 猜测会导致 400 InvalidConnectionDetails / 400 InvalidCredentialDetails。
  • 在生成的示例或已提交的脚本中使用明文凭据 — 切勿呈现或提交明文凭据值。在生成的连接请求体中仅显示 passwordReference / keyReference / tokenReference / servicePrincipalSecretReference。
  • 将本地网关连接请求体模板化为明文 — OnPremisesGateway 需要使用每个网关成员对应的 RSA 加密凭据。
  • 将已发布的单源数据流原地转换为多源数据流 — 绑定会逐渐变得不一致;应新建数据流并停用旧数据流。
  • 通过 updateDefinition 持久化未经预览的候选 M — executeQuery 的速度明显快于“先 updateDefinition,再调试刷新”的循环。参见 mashup-preview.md。
  • 对生产规模的数据源进行无界预览 — executeQuery 会返回完整的计算后数据集。在仅用于预览的文档中加入 Table.FirstN / TOP N / 日期谓词;保存前移除。参见 mashup-preview.md § 必须避免的情况。
  • 混淆 executeQuery 和 EvaluateQuery。 EvaluateQuery 需要先成功刷新;executeQuery + customMashupDocument 则不需要。使用 executeQuery 进行创作预览循环。
  • 在 Windows/PowerShell 上内联使用 --body — cmd.exe 会破坏引号;始终使用 --body "@$env:TEMP\<name>.json"。
建议
  • 使用携带实际 M + connections[] 的单次 updateDefinition,而不是先绑定再保存的两步操作——可减少一次 HTTP 往返;两种方式在功能上等效。在教学演示中,或当引导用的 M 需要与生产环境中的 M 不同时(例如 mashup-preview.md 中的引导分支),使用两步形式。
  • 优先使用 az rest,而不是原始 curl——它会自动处理令牌获取和刷新。只有在需要捕获响应标头时(例如 202 LRO Location)才退而使用 curl——az rest 无法做到这一点。
  • 在 updateDefinition 之前调用 getDefinition——先读取再修改并写入,可防止意外丢失数据;updateDefinition 会完整替换定义。
  • 在 updateDefinition 中使用 ?updateMetadata=true——确保应用 .platform 更改(显示名称)。
  • 使用 jq 操作 JSON——以编程方式构建定义负载。
  • 在作业执行中将参数类型设为 "Automatic"——让引擎根据定义推断类型。
  • 使用环境变量(WS_ID、DF_ID、API、RESOURCE),以便重复使用脚本。
  • 批量验证连接——在刷新前,遍历 queryMetadata.json connections[] 并对每个连接调用 GET /v1/connections/{id};也可选择调用 POST /v1/connections/{id}/testConnection,以检测凭据是否已轮换。
  • 在提交新的数据流之前提供预览图表——将示例数据渲染为 ASCII 图表,让用户验证输出的形状和值。
故障排除
症状解决方法
401 Unauthorized确认 az login 处于有效状态;检查 --resource "https://api.fabric.microsoft.com"(或 PBI v2 使用 https://analysis.windows.net/powerbi/api,末尾不要带斜杠)。
getDefinition 返回 405 Method Not Allowed使用 POST,不要使用 GET。
updateDefinition 静默丢弃查询发送全部 3 个部分(mashup.pq、queryMetadata.json、.platform)。
executeQuery → 400 DataflowExecuteQueryError: Invalid query name请求体使用 {"queries":[…]} 数组格式——这种格式总会失败。改用顶层的 {"QueryName":"<shared>"}(规范形式为 PascalCase;传输时字段名不区分大小写)。
executeQuery → 400 DataflowExecuteQueryError: ErrorCode: QueryNotFoundQueryName 的值与已持久化的 M 或所提供的 customMashupDocument 中的任何 shared 成员都不匹配。通过 getDefinition 列出查询 → 解码 mashup.pq。
成功绑定后,GET /items/{id}/connections 返回 0该端点反映的是刷新后物化的状态,而非定义。通过 getDefinition 验证 → 解码 queryMetadata.json.connections[]。
从 gatewayClusterDatasources/{id} 获取 ClusterId 时返回 404 / PowerBIEntityNotFound按 ID 查询的路由无法解析云连接。使用列表并筛选:GET .../gatewayClusterDatasources --query "value[?id=='$CONN_ID'] | [0].clusterId",受众为 https://analysis.windows.net/powerbi/api(末尾不带斜杠)。新建的连接可能需要 5-15 s 才会显示——请重试。请参阅 connection-management.md § 解析 ClusterId。
updateDefinition 后首次运行刷新失败(数据过期、缺少更改)在任何定义更改后的首次刷新中,请求体必须包含 {"executionData":{"executeOption":"ApplyChangesIfNeeded"}}。
刷新失败并显示“Connection not found”从 queryMetadata.json 中提取 connectionId(复合 ID),解析 DatasourceId,并通过 GET /v1/connections/{id} 确认。
updateDefinition 后缺少 connections[]读取-修改-写入操作从不含绑定的快照重建了 queryMetadata.json。请重新绑定并再次执行 updateDefinition,然后再刷新。
创建并绑定后,刷新报告“connection not found”queryMetadata.json 中的 ID 格式错误。REST id 是普通 GUID;connectionId 是字符串化的复合对象 {"ClusterId":"…","DatasourceId":"…"}。
formatVersion 不匹配错误在 queryMetadata.json 中设置 formatVersion: "202502"。
未启用快速复制在 mashup.pq 的 section 前添加 [StagingDefinition = [Kind = "FastCopy"]]。
LRO 轮询返回 404使用 Location 标头中的 URL——不要手动构造操作 URL。
429 Too Many Requests遵循 Retry-After;使用指数退避。
Base64 解码结果乱码去掉末尾换行符;使用 base64 -w0(Linux)。
Windows 上内联 --body "<json>" 返回 400 / 空请求体启动 az.exe 时,cmd.exe 参数解析器会破坏引号。将内容写入 $env:TEMP\body.json(UTF-8,无 BOM),并传入 --body "@$env:TEMP\body.json"。请参阅 authoring-script-templates.md § PowerShell — 使用定义创建 Dataflow。
刷新失败并显示 EntityUserFailure /“Something went wrong”,且没有详细信息(1) 确认创建后调用了 updateDefinition;(2) 检查凭据类型——在无人值守刷新时,OAuth2+singleSignOnType: None 通常会因租户条件访问而失败;优先使用 WorkspaceIdentity/ServicePrincipal;(3) 对该 dataflow 执行 executeQuery,以隔离 M 和数据源问题;(4) GET https://api.powerbi.com/v1.0/myorg/groups/{ws}/dataflows/{df}/transactions(PBI v1.0)有时会返回更详细的实体级错误。

示例

平台说明 — 以下示例使用 bash。在 Windows / PowerShell 上,bash 模式(MASHUP='...' heredoc、echo -n | base64 -w0、tr -d '\r' | grep -i location | awk)会带来实际的转义难题,也容易导致刷新模式出现间歇性故障。下方两个最容易遇到问题的示例(创建和刷新)提供了 PowerShell 变体链接。完整的 PowerShell 模板(创建、刷新、验证连接、绑定连接、创建云连接):authoring-script-templates.md § PowerShell。在 PowerShell 上,建议使用 --body "@$env:TEMP\body.json",并通过 [IO.File]::WriteAllText($path, $body, [System.Text.UTF8Encoding]::new($false)) 写入请求正文,而不要使用 Out-File(它会在 Windows PowerShell 5.1 中写入 UTF-8 BOM,导致 az.exe 无法解析请求正文),也不要使用内联 --body "{...}"(cmd.exe 会破坏其中的内容)。

示例 1:从头创建 Dataflow Gen2

提示词:“创建一个新的 Dataflow Gen2,通过 Web 连接器读取公开的 CSV,并验证它。”

Agent 响应 — 工作流 A 的可运行 bash 实现。PowerShell 变体:authoring-script-templates.md § 端到端冒烟测试。

# Prereqs: az login, jq, base64, uuidgen. Workspace must support Dataflow Gen2.
WS_ID="<workspaceId>"
DF_NAME="my-titanic-df"
CONN_NAME="my-titanic-web-conn"
URL="https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
RES="https://api.fabric.microsoft.com"; API="$RES/v1"
PBI="https://analysis.windows.net/powerbi/api"   # NO trailing slash

# Step 1: List existing connections, try to reuse by name.
CONN_ID=$(az rest --method get --resource "$RES" --url "$API/connections" \
  --query "value[?displayName=='$CONN_NAME'] | [0].id" -o tsv)

# Step 2: Create if missing (Web + Anonymous; see connection-management.md for other shapes).
if [ -z "$CONN_ID" ] || [ "$CONN_ID" = "null" ]; then
  BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp)  # GNU + BSD/macOS compatible
  cat > "$BODY_FILE" <<EOF
{
  "displayName": "$CONN_NAME",
  "connectivityType": "ShareableCloud",
  "connectionDetails": {
    "type": "Web", "creationMethod": "Web",
    "parameters": [{"name": "url", "dataType": "Text", "value": "$URL"}]
  },
  "privacyLevel": "Organizational",
  "credentialDetails": {
    "singleSignOnType": "None", "connectionEncryption": "NotEncrypted",
    "skipTestConnection": false,
    "credentials": {"credentialType": "Anonymous"}
  }
}
EOF
  CONN_ID=$(az rest --method post --resource "$RES" --url "$API/connections" \
    --headers "Content-Type=application/json" --body "@$BODY_FILE" --query id -o tsv)
  rm -f "$BODY_FILE"
fi

# Step 3: Resolve ClusterId via list+filter; retry — PBI v2 lags by 5-15s on new conns.
for i in 1 2 3 4 5 6 7 8; do
  CLUSTER_ID=$(az rest --method get --resource "$PBI" \
    --url "https://api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources" \
    --query "value[?id=='$CONN_ID'] | [0].clusterId" -o tsv 2>/dev/null)
  [ -n "$CLUSTER_ID" ] && [ "$CLUSTER_ID" != "null" ] && break
  sleep $((i*3))
done
# Fail-fast: an empty ClusterId silently corrupts the composite connectionId and the
# resulting updateDefinition / refresh failures are hard to debug. Stop here instead.
if [ -z "$CLUSTER_ID" ] || [ "$CLUSTER_ID" = "null" ]; then
  echo "FAIL: ClusterId not resolved for $CONN_ID after retries. Verify the connection is visible at PBI v2 (api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources)." >&2
  exit 1
fi

# Step 4: Create empty dataflow shell (sync 201).
SHELL_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"displayName":"%s"}' "$DF_NAME" > "$SHELL_BODY"
DF_ID=$(az rest --method post --resource "$RES" \
  --url "$API/workspaces/$WS_ID/dataflows" \
  --headers "Content-Type=application/json" \
  --body "@$SHELL_BODY" --query id -o tsv)
rm -f "$SHELL_BODY"

# Step 5: One-shot updateDefinition — real M + composite-bound connections[] + .platform.
MASHUP='section Section1;
shared Titanic = let
    Source = Csv.Document(Web.Contents("'"$URL"'"), [Delimiter=",", Encoding=65001, QuoteStyle=QuoteStyle.Csv]),
    Headers = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
in Headers;'

COMPOSITE_ID="{\"ClusterId\":\"$CLUSTER_ID\",\"DatasourceId\":\"$CONN_ID\"}"
QUERY_META=$(jq -n --arg name "$DF_NAME" --arg cid "$COMPOSITE_ID" --arg url "$URL" --arg qid "$(uuidgen)" '{
  formatVersion: "202502",
  name: $name,
  queriesMetadata: { Titanic: { queryId: $qid, queryName: "Titanic" } },
  connections: [ { connectionId: $cid, kind: "Web", path: $url } ]
}')
PLATFORM=$(jq -n --arg name "$DF_NAME" --arg lid "$(uuidgen)" '{
  "$schema": "https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json",
  metadata: { type: "Dataflow", displayName: $name },
  config: { version: "2.0", logicalId: $lid }
}')

MASHUP_B64=$(echo -n "$MASHUP" | base64 -w0)
META_B64=$(echo -n "$QUERY_META" | base64 -w0)
PLAT_B64=$(echo -n "$PLATFORM" | base64 -w0)

BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp)  # GNU + BSD/macOS compatible
cat > "$BODY_FILE" <<EOF
{"definition":{"parts":[
  {"path":"mashup.pq",          "payload":"${MASHUP_B64}", "payloadType":"InlineBase64"},
  {"path":"queryMetadata.json", "payload":"${META_B64}",   "payloadType":"InlineBase64"},
  {"path":".platform",          "payload":"${PLAT_B64}",   "payloadType":"InlineBase64"}
]}}
EOF
az rest --method post --resource "$RES" \
  --url "$API/workspaces/$WS_ID/dataflows/$DF_ID/updateDefinition?updateMetadata=true" \
  --headers "Content-Type=application/json" --body "@$BODY_FILE"
rm -f "$BODY_FILE"

# Step 6: Verify connections[] persisted via getDefinition (NOT /items/{id}/connections).
# Assumes the sync 200 fast-path (typical, ~1s). If the call ever returns 202 LRO,
# az rest can't expose the Location header — switch to the curl + poll pattern from
# Example 3 / authoring-script-templates.md and decode the polled 200 body instead.
PERSISTED=$(az rest --method post --resource "$RES" \
  --url "$API/workspaces/$WS_ID/dataflows/$DF_ID/getDefinition" \
  --headers "Content-Length=0" \
  | jq -r '.definition.parts[] | select(.path=="queryMetadata.json") | .payload' | base64 -d \
  | jq -r '.connections | length')
[ "${PERSISTED:-0}" -gt 0 ] && echo "OK: connections[] persisted." || { echo "FAIL: bind missing (or getDefinition returned a 202 LRO body — see note above)." >&2; exit 1; }

# Step 7 (optional): Validate the M evaluates — top-level QueryName, PascalCase.
EQ_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"QueryName":"Titanic"}' > "$EQ_BODY"
az rest --method post --resource "$RES" \
  --url "$API/workspaces/$WS_ID/dataflows/$DF_ID/executeQuery" \
  --headers "Content-Type=application/json" \
  --body "@$EQ_BODY" --output-file /tmp/titanic.arrow
rm -f "$EQ_BODY"
# Apache Arrow stream — embedded {"Error":"..."} means failure even on HTTP 200.
grep -q '"Error":"' /tmp/titanic.arrow && { echo "executeQuery surfaced an error." >&2; exit 1; }

# Step 8 (optional): Trigger refresh with ApplyChangesIfNeeded on first run — see Example 2.
示例 2:触发刷新作业

提示词:“触发此 dataflow 的刷新,并轮询直到完成。”

Agent 响应:

# Trigger refresh (returns 202 + Location header for polling).
# jobType MUST be "Refresh"; "Pipeline" returns 400 InvalidJobType.
# On the first refresh after any updateDefinition, body MUST include executeOption=ApplyChangesIfNeeded
# (otherwise Fabric refreshes the previously-applied definition).
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = https://api.fabric.microsoft.com).
LOCATION=$(curl -sS -X POST \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data '{"executionData":{"executeOption":"ApplyChangesIfNeeded"}}' \
  "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/dataflows/${DF_ID}/jobs/instances?jobType=Refresh" \
  -o /dev/null -D - | tr -d '\r' | grep -i "^location:" | awk '{print $2}')

# Poll while the status is non-terminal. Fabric refresh job status enum:
#   NotStarted / InProgress   -> non-terminal, keep polling
#   Completed                 -> success
#   Failed / Cancelled        -> terminal backend outcome (fatal-stop)
#   Deduped                   -> another refresh is already running; this trigger was skipped (NOT success)
# Treat ONLY NotStarted/InProgress as non-terminal and break on anything else, so a newly-added
# terminal status surfaces immediately instead of waiting out MAX_POLLS (the contract notes more
# status values may be added over time). MAX_POLLS bounds the wait if the job never terminates.
ATTEMPT=0; MAX_POLLS="${MAX_POLLS:-60}"
while [ "$ATTEMPT" -lt "$MAX_POLLS" ]; do
  STATUS=$(az rest --method get --url "$LOCATION" \
    --resource "https://api.fabric.microsoft.com" --query "status" -o tsv)
  echo "Status: $STATUS"
  case "$STATUS" in NotStarted|InProgress) ;; *) break ;; esac
  sleep 10; ATTEMPT=$((ATTEMPT + 1))
done
case "$STATUS" in
  Completed) : ;;  # success (exit 0)
  Deduped)
    # Concurrency, not success: another refresh is already running and this trigger was skipped.
    # Monitor the in-flight instance instead of re-triggering. Exit non-zero so automation does not
    # mistake a skipped trigger for a completed refresh.
    echo "Refresh deduplicated — another instance is already running; monitor that instance instead of re-triggering."
    exit 2 ;;
  NotStarted|InProgress)
    # Max-poll bound reached before any terminal status — a polling timeout, NOT a terminal outcome.
    # Surface the raw job instance and stop; do not assume success.
    echo "Polling stopped after ${MAX_POLLS} attempts with non-terminal status '$STATUS' (max-poll timeout, not a terminal outcome)."
    az rest --method get --url "$LOCATION" --resource "https://api.fabric.microsoft.com"
    exit 1 ;;
  *)  # Failed / Cancelled (or any other terminal status): a terminal backend outcome — not something
      # to debug-loop. Surface the job's raw error (the job-instance body's .failureReason, an
      # ErrorResponse with .errorCode / .isRetriable / .message) and STOP. Do NOT re-trigger or keep
      # polling when .failureReason.isRetriable=false or the error is workspace-wide.
      az rest --method get --url "$LOCATION" --resource "https://api.fabric.microsoft.com"
      exit 1 ;;
esac

PowerShell 变体(Invoke-WebRequest 可直接获取响应头;无需使用 tr | grep | awk 管道):

# Notes:
# - $Resp.Headers["Location"] returns string or string[] depending on PS version — never
#   use .Location[0] (returns first character on Windows PS 5.1 plain-string case).
# - Wrap Invoke-WebRequest in try/catch on 5.1 (-SkipHttpErrorCheck is PS 7+).
# - Fabric refresh job status enum: NotStarted / InProgress (non-terminal); Completed (success);
#   Failed / Cancelled (fatal); Deduped (another refresh already running — NOT success). Treat only
#   NotStarted/InProgress as non-terminal and break on anything else, bounded by a max-poll count, so
#   a newly-added terminal status surfaces immediately instead of waiting out $MaxPolls.
#   This is distinct from the LRO operation enum (Running / Succeeded / Failed / Cancelled).
#   Refresh "success" = "Completed", not "Succeeded".
# Acquire $Token per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = https://api.fabric.microsoft.com).
try {
  $Resp = Invoke-WebRequest -Method POST -UseBasicParsing `
    -Uri "https://api.fabric.microsoft.com/v1/workspaces/$WS_ID/dataflows/$DF_ID/jobs/instances?jobType=Refresh" `
    -Headers @{ Authorization = "Bearer $Token"; "Content-Type" = "application/json" } `
    -Body '{"executionData":{"executeOption":"ApplyChangesIfNeeded"}}'
} catch {
  Write-Error "Refresh trigger failed: $($_.Exception.Message)"; exit 1
}
$Location = $Resp.Headers["Location"]
if ($Location -is [array]) { $Location = $Location[0] }

$Attempt = 0; $MaxPolls = 60
do {
  $Status = az rest --method get --url $Location `
    --resource "https://api.fabric.microsoft.com" --query "status" -o tsv
  Write-Host "Status: $Status"
  if ($Status -notin 'NotStarted','InProgress') { break }
  Start-Sleep -Seconds 10; $Attempt++
} while ($Attempt -lt $MaxPolls)
if ($Status -in 'NotStarted','InProgress') {
  # Max-poll bound reached before any terminal status — a polling timeout, NOT a terminal outcome.
  Write-Host "Polling stopped after $MaxPolls attempts with non-terminal status '$Status' (max-poll timeout, not a terminal outcome)."
  az rest --method get --url $Location --resource "https://api.fabric.microsoft.com"
  exit 1
}
switch ($Status) {
  'Completed' { }  # success (exit 0)
  'Deduped' {
    # Concurrency, not success: another refresh is already running and this trigger was skipped.
    # Exit non-zero so callers don't treat a skipped trigger as a completed refresh.
    Write-Host "Refresh deduplicated — another instance is already running; monitor that instance instead of re-triggering."
    exit 2
  }
  default {
    # Failed / Cancelled (or any other terminal status): a terminal backend outcome — surface the job's
    # raw error (the job-instance body's .failureReason: .errorCode / .isRetriable / .message) and STOP;
    # do not re-trigger or debug-loop when failureReason.isRetriable=false or the error is workspace-wide.
    az rest --method get --url $Location --resource "https://api.fabric.microsoft.com"
    Write-Error "Refresh terminated '$Status' (not Completed)"; exit 1
  }
}
示例 3:修改现有 Dataflow 的定义

提示词:“使用修改后的查询更新现有 dataflow 的 mashup。”

Agent 响应 — 读取、修改、写入循环。通常情况下,getDefinition 会同步返回 200;此模板也处理 202 + LRO 分支。

RESOURCE="https://api.fabric.microsoft.com"
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = $RESOURCE).

# 1. Read current definition (sync 200 or 202 LRO — handle both).
HDR=$(mktemp); BODY=$(mktemp)
CODE=$(curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Length: 0" \
  "$RESOURCE/v1/workspaces/${WS_ID}/dataflows/${DF_ID}/getDefinition" \
  -D "$HDR" -o "$BODY" -w "%{http_code}")
if [ "$CODE" = "202" ]; then
  LOC=$(tr -d '\r' < "$HDR" | grep -i "^location:" | awk '{print $2}')
  RETRY=$(tr -d '\r' < "$HDR" | grep -i "^retry-after:" | awk '{print $2}'); RETRY=${RETRY:-5}
  while :; do
    sleep "$RETRY"
    OP=$(az rest --method get --resource "$RESOURCE" --url "$LOC")
    case "$(echo "$OP" | jq -r '.status // empty')" in
      Succeeded) DEF=$(az rest --method get --resource "$RESOURCE" --url "${LOC%/}/result"); break ;;
      Failed|Cancelled) echo "ERROR: getDefinition $(echo "$OP" | jq -r '.status')" >&2; exit 1 ;;
    esac
  done
else
  DEF=$(cat "$BODY")
fi
rm -f "$HDR" "$BODY"

# 2. Decode each part, modify mashup.pq, re-encode all 3.
MASHUP=$(echo "$DEF" | jq -r '.definition.parts[] | select(.path=="mashup.pq")          | .payload' | base64 -d)
META=$(  echo "$DEF" | jq -r '.definition.parts[] | select(.path=="queryMetadata.json") | .payload' | base64 -d)
PLAT=$(  echo "$DEF" | jq -r '.definition.parts[] | select(.path==".platform")          | .payload' | base64 -d)

NEW_MASHUP=$(echo "$MASHUP" | sed 's/old-pattern/new-pattern/')   # edit M here

MASHUP_B64=$(echo -n "$NEW_MASHUP" | base64 -w0)
META_B64=$(echo -n "$META"        | base64 -w0)
PLAT_B64=$(echo -n "$PLAT"        | base64 -w0)

# 3. Build the updateDefinition body in a temp file (full replacement — all 3 parts).
BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp)  # GNU + BSD/macOS compatible
cat > "$BODY_FILE" <<EOF
{"definition":{"parts":[
  {"path":"mashup.pq",          "payload":"${MASHUP_B64}", "payloadType":"InlineBase64"},
  {"path":"queryMetadata.json", "payload":"${META_B64}",   "payloadType":"InlineBase64"},
  {"path":".platform",          "payload":"${PLAT_B64}",   "payloadType":"InlineBase64"}
]}}
EOF
az rest --method post --resource "$RESOURCE" \
  --url "$RESOURCE/v1/workspaces/${WS_ID}/dataflows/${DF_ID}/updateDefinition?updateMetadata=true" \
  --headers "Content-Type=application/json" --body "@$BODY_FILE"
rm -f "$BODY_FILE"

要绑定新连接?示例 1(步骤 1-5)是规范的绑定并保存流程。仅绑定的操作指南见 authoring-cli-quickref.md § 连接绑定快速模式 和 authoring-script-templates.md § 连接绑定模板。


输出要求

此技能完成任务后,Agent 应返回:

字段规范
详细程度用 3–10 行简洁总结创建或修改的内容。
默认格式状态报告使用 Markdown;单资源响应使用带围栏的 JSON 代码块;列表响应使用 Markdown 表格。
副作用披露明确报告已创建、修改或删除的 ID,以及目标工作区 ID。没有 ID 时,绝不可暗示操作成功。保存或替换数据流定义时,应在正文中列出写入的部分——mashup.pq、queryMetadata.json、.platform——因为长命令正文会在记录中被截断,否则这些部分名称就会丢失。还要注明实际使用的持久化路径:通过该端点保存定义时使用 updateDefinition;创建时提供定义则使用创建调用 POST /v1/workspaces/{ws}/dataflows。如果定义是通过创建 POST 持久化的,切勿声称使用了 updateDefinition。
验证重新 GET 受影响的资源(数据流、连接、作业实例),并在宣布完成前说明其状态(例如 provisionState、status、Completed)。
错误处理如果任何步骤返回非 2xx 状态、LRO 状态为 Failed/Cancelled,或 Arrow 流返回 {"Error":"..."},则原样传递原始错误并停止。终态刷新结果为 Failed/Cancelled、isRetriable: false 的后端错误,或工作区范围的 UnknownException,都属于致命停止情况——报告后结束;不要重新触发、重新轮询或进入冗长的调试循环。
预览渲染(工作流 C)执行 executeQuery 后,在聊天中将结果的 head(10) 渲染为 Markdown 表格,并同时提供已保存的 Arrow 文件——即使嵌入错误检查通过也不例外。这样可以捕获静默成功的错误(筛选器删除了所有行、列错误、偏移量差一、类型转换错误),这些错误无法通过嵌入错误检测器发现。代码片段与抑制规则:dataflows-consumption-cli § 示例 5b。
API 名称答案提及 API 端点或请求正文中的字段时,使用其准确且区分大小写的名称(executeQuery、customMashupDocument、QueryName、mashup.pq、queryMetadata.json、GET /v1/connections/supportedConnectionTypes),不要使用改述或复数形式。

评论

支持 Markdown