关于 dataflows-authoring-cli
面向 Microsoft Fabric Dataflows Gen2 编写的命令行技能,指导 Agent 创建、修改、预览和验证数据流,并可选择刷新。变更前要求先明确需求。
主要功能
- 01端到端创建数据流
- 02修改现有数据流
- 03预览候选 M 查询
- 04管理输出目标
- 05验证定义与连接绑定
- 06按需刷新数据流
适用场景
- 01按指定来源和转换创建 Dataflow
- 02更新现有数据流定义
- 03保存更改前预览查询结果
- 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.mdauthoring-script-templates.mdconnection-management.mdconnectors.mdm-language.mdmashup-preview.mdoutput-destinations.mdreferences/charts/bar_chart.pyline_chart.py详细介绍
更新检查 — 每个会话仅执行一次(强制) 首次在会话中使用此技能时,继续操作前先运行 check-updates 技能。
- GitHub Copilot CLI / VS Code:调用
check-updates技能。- Claude Code / Cowork / Cursor / Windsurf / Codex:比较本地与远程的 package.json 版本。
- 如果本会话中已执行过检查,则跳过。
重要注意事项
- 若要根据工作区名称查找工作区详情(包括其 ID):列出所有工作区,然后使用 JMESPath 进行筛选。
- 若要根据工作区 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.md | M 端源连接器:经实时验证的函数清单、Lakehouse 深度导航、Web.Page / Web.BrowserContents 的网关范围,以及 Html.Table / Csv.Document / Json.Document 模式 |
| m-language.md | Dataflow Gen2 的 M 语言语义:try 记录结构、列转换中的单元格级错误包装、行与子表上下文中的 each 作用域、可选字段访问 [?] / Record.FieldOrDefault、带引号的标识符、沙盒禁用的符号(File.Contents) |
| mashup-preview.md | executeQuery 契约:引导分支、自动包装规则、严格避免无界预览 |
| output-destinations.md | 输出目标模式:Lakehouse Table、Lakehouse Files、Warehouse、ADX、Azure SQL。DataDestinations 注释、隐藏查询、loadEnabled 规则、连接限制 |
常用参考资料(位于 ../../common/)
| 文件 | 阅读时机 |
|---|---|
| COMMON-CLI.md | az login、令牌获取、az rest、分页、LRO 轮询、CLI 注意事项。§ 在 Fabric 中查找工作区和项目为必读内容。 |
| COMMON-CORE.md | Fabric 拓扑、环境 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. 端到端创建新数据流 — 发现或创建连接、创建数据流、保存 M 和绑定、验证,并可选择刷新。
- B. 修改现有数据流 — 读取、修改并写入定义;采用标准的“发现 → 制定 → 执行 → 验证”循环。
- C. 预览驱动的创作循环 — 通过
executeQuery迭代候选 M,之后再通过updateDefinition持久化。 - D. 输出目标 — 通过
DataDestinations注释将查询结果写入 Lakehouse(表/文件)、Warehouse、ADX 或 Azure SQL。完整参考:output-destinations.md。
A. 端到端创建新数据流
当数据流尚不存在时使用此工作流。涵盖完整的顺利流程:发现或创建连接、创建数据流框架、在一次 updateDefinition 中保存 M 和绑定、验证,并可选择刷新。
步骤:
- 列出现有连接,并按
connectionDetails.type和目标 URL/主机进行筛选——如果存在匹配项,则重复使用(GET /v1/connections+ JMESPath)。 - 如果没有匹配项,则创建连接。 首先调用
GET /v1/connections/supportedConnectionTypes,以发现必需参数和支持的凭据类型,然后调用POST /v1/connections(同步返回 201)。请求体结构和凭据架构:connection-management.md。 - 解析复合绑定所需的
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。 - 创建数据流外壳。 调用
POST /v1/workspaces/{ws}/dataflows并传入{"displayName":"<displayName>"},同步返回 201。创建时definition字段为可选项,可在下一步设置。如果改为在此创建 POST 中提供完整的definition(包含全部三个部分),则此调用就是持久化入口——在摘要中将POST /v1/workspaces/{ws}/dataflows(而非updateDefinition)列为持久化路径。 - 一次调用保存 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 和 LROLocation——两种情况都要处理。 - 验证绑定已持久化。 再次调用
getDefinition,解码queryMetadata.json,并确认connections[]保持完整。验证时不要使用GET /items/{id}/connections——该端点反映的是刷新后物化的状态,而非已持久化的定义,即使绑定成功后也会返回 0。参见 AVOID。 仅用于预览的受限回退方案: 如果 Power BI v2gatewayClusterDatasources返回 401/403/Unauthorized,或重试后仍看不到ClusterId,导致无法继续绑定连接,请勿在创建数据流之前停止。创建外壳,保存查询定义但不要声称源绑定有效;对每个已保存的查询仅使用QueryName调用executeQuery,从 Arrow 流中显示确切的凭据或绑定错误,不要编造图表数据,也不要刷新。 - (建议)提供 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。 - (可选)触发刷新以物化数据。调用
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,再执行完整的 MupdateDefinition。两种方式都可行——请参阅 PREFER。
B. 修改现有数据流
当数据流已存在时使用此流程。标准的“发现 → 制定 → 执行 → 验证”循环。如果数据流尚不存在,请改用 工作流 A。
- 发现 — 列出工作区、列出数据流,并调用
getDefinition(解码mashup.pq和queryMetadata.json)。通过GET /v1/connections/{id}验证所有connections[]条目。 - 制定 — 修改 M、重新编码各部分,并确保调用方的连接存储中存在每个被引用的
connectionId。 - 执行 — 使用全部 3 个部分(完整替换)调用
POST .../updateDefinition?updateMetadata=true。可选择触发刷新。 - 验证 — 再次调用
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。
关键要求:
- 源查询必须包含
[DataDestinations = {[...]}]注释,并通过名称引用目标查询。 - 隐藏的目标查询(后缀为
_DataDestination)使用空值安全运算符?[Data]?(表)或?[Content]?(文件)导航到目标存储。 - queryMetadata.json 必须在目标查询上设置
"loadEnabled": false,否则刷新会失败。在摘要中使用字面部分名称说明这一点(例如:“在queryMetadata.json的目标查询上设置loadEnabled: false”)。 - 始终对通过 API 创建的数据流使用
IsNewTarget = true,即使目标表已存在。 - 绑定相应的连接(Lakehouse:种类为
"Lakehouse";Warehouse:种类为"Warehouse";ADX:种类为"AzureDataExplorer";Azure SQL:种类为"Sql"),并使用组合的ClusterId/DatasourceIdID。 - 首次刷新必须使用
ApplyChangesIfNeeded来发布草稿并协调注释。 - 所有源列都必须指定类型——所有目标类型都不接受
Any类型的列。 - **在书面摘要中注明定义部分的名称。**由于 CLI 会话记录会截断较长的命令正文,最终摘要(正文,而不只是 shell 命令)必须使用其字面路径注明三个定义部分——
mashup.pq、queryMetadata.json和.platform,以确保答案中保留部分名称(例如:“通过updateDefinition保存了mashup.pq+queryMetadata.json+.platform”)。不要将queryMetadata.json缩写为“query metadata”,也不要将内部字段queriesMetadata用作替代。
支持的目标:
| 目标 | 连接种类 | 目标查询函数 | 备注 |
|---|---|---|---|
| Lakehouse 表 | Lakehouse | Lakehouse.Contents(...) | 路径:"Lakehouse" |
| Lakehouse 文件 | Lakehouse | Lakehouse.Contents(...) | TypeSettings = [Kind = "File"]、?[Content]? |
| Warehouse | Warehouse | Fabric.Warehouse(...) | 路径:"Warehouse",Schema/Item 导航 |
| Azure Data Explorer | AzureDataExplorer | AzureDataExplorer.Contents(...) | 路径必须与连接完全一致(包括末尾斜杠!) |
| Azure SQL | Sql | Sql.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 和 LROLocation——两种情况都要处理。参见 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 LROLocation)才退而使用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: QueryNotFound | QueryName 的值与已持久化的 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),不要使用改述或复数形式。 |