关于 platform-datamask-run
此 Skill 用于在 Salesforce 沙盒组织中运行 Data Mask,涵盖策略设置、任务执行和脱敏记录报告。它也可取消进行中的任务,并区分策略配置与任务状态所用的 API。
主要功能
- 01创建或查找脱敏策略
- 02配置对象及字段处理方式
- 03启动沙盒脱敏任务
- 04轮询任务直至结束
- 05报告各对象的脱敏数量
- 06中止正在运行的任务
适用场景
- 01在 Salesforce 沙盒中脱敏 PII
- 02运行现有脱敏策略
- 03查看各对象脱敏结果
- 04取消正在运行的脱敏任务
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-datamask-run安装后在 Claude Code 中输入/platform-datamask-run确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-surface.mdpolicy-authoring.mdrun-and-abort.mdscripts/poll-job.sh详细介绍
platform-datamask-run:Salesforce Data Mask 端到端操作
使用此技能在沙盒上操作 Salesforce Data Mask 功能:针对 PII 字段配置屏蔽策略,启动屏蔽作业,轮询至作业进入终态,报告已屏蔽的记录,并中止仍在进行的运行。
Data Mask 仅适用于沙盒——在生产环境中,运行/中止 REST 端点会返回 403(运行时沙盒保护)。启动前请确认目标组织是沙盒。
此技能适用的任务
- 针对已配置的策略运行 Data Mask 作业
- 轮询屏蔽作业状态,直至完成
- 报告已屏蔽记录数量 / 每个对象的结果
- 中止(取消)正在进行的屏蔽运行
- 创建或识别作业所依据的策略
以下情况请委派给其他技能:
- 手写匿名化 Apex →
platform-apex-generate - 填充或生成测试数据 →
platform-data-manage - 部署无关元数据 →
platform-metadata-deploy
首先必须弄清楚的一件事:API 表面映射
最容易导致失败的情况,就是以为 Data Mask 实体是普通的数据 API 对象。事实并非如此,而且不同实体的接口各不相同。 开始操作前请记住下表——在这里猜错,会让一个 3 秒钟的作业变成 30 分钟的死局。
| 实体 | 说明 | 访问方式 |
|---|---|---|
DataMaskPolicy | 屏蔽策略框架(配置) | Tooling API 或 Metadata API(精简框架:仅包含 <label>/<description>/<runOnRefresh>)——不支持标准 SOQL/sobject describe |
DataMaskPolicyObject | 策略针对的对象(包含可选的行筛选条件) | 仅限 Tooling API——支持查询和插入;行子集“样本”运行在此处设置 FilterEnabled+WhereCriteria(策略上没有 sampleSize) |
DataMaskPolicyField | 字段及其屏蔽处理方式 | 仅限 Tooling API——支持查询和插入;处理方式列为 MaskingCategory + MaskValue |
DataMaskPolicyJobRun | 作业(一次屏蔽运行) | 标准 SOQL——sf data query 可用 |
DataMaskPolicyJobRunDtl | 每个对象的作业详情(子项,外键为 DataMaskPolicyJobRunId) | 标准 SOQL |
| 启动运行 | — | REST 运行 API POST /services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run |
| 中止运行 | — | REST 运行 API POST /services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort |
具体来说:
sf sobject describe --sobject DataMaskPolicy→NOT_FOUND(不要再用标准 API 重试)- 通过
sf data query执行SELECT ... FROM DataMaskPolicy→INVALID_TYPE - 通过 Tooling 查询策略:
sf data query --use-tooling-api --query "SELECT Id, MasterLabel FROM DataMaskPolicy" - 通过标准 API 查询作业 / 作业详情:
sf data query --query "SELECT Id, Status FROM DataMaskPolicyJobRun"
完整命令参考:references/api-surface.md。
选择与请求相符的工作流
此技能有两个不同的工作流。根据用户的请求,先选择一个,然后 执行该工作流的每一个步骤——没有可选步骤:
| 用户想要…… | 执行 | 结束条件 |
|---|---|---|
| 配置/编辑策略并掩码处理记录;报告掩码处理了多少条记录 | 工作流 A — 掩码处理并报告(见下文) | 根据详细行报告掩码处理的数量 |
| 取消/中止一次掩码处理运行 | 工作流 B — 取消运行(见下文) | 确认作业状态为 canceled |
根据请求中的动词进行选择。“创建/编辑策略并运行”、“掩码处理 PII”、“掩码处理了多少条记录” → 仅执行工作流 A。“中止”、“取消”、“停止运行” → 工作流 B。掩码处理并报告的请求不包含中止操作:不要为了“演示”取消而启动第二个作业——未经请求的运行会白白耗费一个完整的约 5–10 分钟作业(见 A4 中的池下限),这是导致任务在报告所要求的掩码处理数量之前就耗尽回合的首要原因。
工作流 A — 掩码处理并报告
A1. 确认沙盒并获取组织上下文
确认组织是沙盒,并获取用于运行 API 调用的实例 URL 和会话令牌:
sf org display --target-org <alias> --json
A2. 查找或创建策略
优先复用现有策略(最快,无需部署):
sf data query --use-tooling-api --target-org <alias> \
--query "SELECT Id, DeveloperName, MasterLabel FROM DataMaskPolicy"
如果没有策略涵盖所需的 Contact PII,则使用两步方案创建一个(DataMaskPolicy 元数据结构只是一个简单框架;成员关系需通过 Tooling 插入):
- 使用 mdapi 格式部署元数据框架(
--metadata-dir+package.xml;以源格式部署的--source-dir会失败并提示“Could not infer a metadata type”)。框架仅包含<label>、<description>、<runOnRefresh>。这会创建一个带有活动修订的策略,这是 A2 的要求。 - 先 Tooling 插入
DataMaskPolicyObject(每个对象一个),再插入其DataMaskPolicyField行。每个字段行的处理方式为MaskingCategory(library)+MaskValue(类似first_name、email、phone的 snake_case 令牌)。没有MaskingRuleType列。
插入顺序很重要:通过 Tooling 创建的父项(没有活动修订)会导致子项插入失败,并返回
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY。请先部署元数据框架。
完整方案和 MaskValue 令牌表请参阅 references/policy-authoring.md。为每个字段选择合适的
MaskValue;不要一概替换。
A3. 启动掩码处理运行(REST 运行 API)
printf '{}' > ./empty-body.json
sf api request rest \
"/services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run" \
--method POST --body @./empty-body.json --target-org <alias>
该端点需要空 JSON 正文({})——即使 API 不接受任何载荷,sf api request rest 也要求 POST 请求带上 --body。使用 @ 前缀传入文件(--body @./empty-body.json);否则会将字面路径作为正文发送,导致 API 返回 JSON_PARSER_ERROR。200 响应会返回 jobRunId、policyId、status(运行 API 的状态为大写,例如 RUNNING)和 message: "Job started successfully"。409/CONFLICT 表示该策略已有正在进行的运行。
现在就写入
report.md,轮询前不要等——不要等到最后才写。 掩码处理作业需要 几分钟(见下文),而这项任务最常见的零分原因,就是轮询期间回合结束,却没有写入任何输出文件。拿到jobRunId后,立即写入report.md,记录目前已知的所有信息(策略 Id/标签、运行命令、jobRunId、状态RUNNING,以及用于掩码处理数量的“正在轮询等待完成……”占位文本)。然后在作业完成后更新同一个文件。已有报告并注明“仍在运行”,也胜过没有文件;捏造数量更糟——只有根据详细行(A5)填写数量。
A4. 轮询至终态(标准 SOQL)
轮询 DataMaskPolicyJobRun.Status,直到其达到终态。不要将运行中的状态报告为最终状态。
- 运行中(尚未开始处理):
pending、scheduled— 作业已排队,但尚不可中止 - 运行中(正在处理):
running— 只有在此状态下才能成功中止 - 终态:
completed、completed_with_errors、failed - 中止目标状态:
canceled(只有一个 “l”)
pending 不等于 running。 对 pending/scheduled 状态的作业执行中止会返回 409 CONFLICT
("Job is not in a running state ... status=PENDING")。必须轮询,直到状态确实为 running,才能中止——请参阅工作流 B。
作业运行缓慢——预计需要数分钟,并使用随附脚本进行轮询。 Data Mask 在后端资源池/调度器上运行,耗时至少约为 5–10 分钟:即使是很小的(20 行)作业,启动后通常也需要数分钟才会进入终态或生成明细行。这是固定开销,与行数不成正比。请据此安排运行——最常见的失败原因是误以为作业会立即完成、以很短的间隔轮询,最终导致超时或写出“仍在等待”的报告。
将 scripts/poll-job.sh 作为单条命令运行——不要手动编写 SOQL 轮询循环:
bash scripts/poll-job.sh <alias> <jobRunId> # defaults: cap 600s (10 min), 20s interval
该脚本会以较低频率的间隔休眠,在出现可信的明细行时立即提前结束,在标准输出中打印终态信号(completed/failed/canceled),并以 0 退出(超时则以 1 退出)。调用一次并读取其结果——不要将其包装在自己的重试循环中,也不要以低于 10 秒的间隔轮询(这只会浪费工具调用次数,而作业不可能更快完成)。
明细行才是事实依据,而不是父级状态。 父级 DataMaskPolicyJobRun.Status
可能会滞后——即使脱敏实际已完成,它也可能仍显示 pending/running 一段时间。一旦存在 total_records_masked(或 completed)DataMaskPolicyJobRunDtl 行,脱敏就已完成。poll-job.sh 已包含所有这些处理逻辑——有上限的轮询间隔和超时时间、在出现事实依据明细行时提前结束,以及终态信号退出码——因此不要在内联代码中重新实现这些逻辑。运行一次轮询脚本,读取其退出信号,然后更新 report.md(轮询前编写的占位文件),填入终态和 A5 中的脱敏记录数。
A5. 根据作业明细对象报告结果
父级作业包含总体状态;每个对象的脱敏记录数保存在子级
DataMaskPolicyJobRunDtl 中(通过 DataMaskPolicyJobRunId 关联)。请报告具体数量,不要编造:
sf data query --target-org <alias> \
--query "SELECT Id, DataMaskPolicyJobRunId, Status FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '<jobRunId>'"
仅报告行中明确显示的内容——不要夸大粒度。 明细行是对象级的 status_update 条目(对象的 loaded、completed、total_records_masked 状态,例如 Contact)。它们不是字段级行。因此,可以将对象级成功表述为观察到的事实(“Contact:已脱敏 27/27 条记录,错误行数为 0”),但应将字段级成功表述为推断,而不是直接观察到的结果——应说“未返回字段级错误行,因此没有字段被报告为失败”,而不是“5 个字段全部成功”(数据中没有字段级成功行支持这一说法)。将推断夸大为观察结果,是这里最常见的事实性错误。
工作流 B — 取消一次运行
当请求是中止/取消脱敏运行时,使用此工作流。它针对当前正在进行的运行——中止是对正在运行的作业按需执行的操作;不会有人为了取消而启动作业。步骤 B1–B4 均为必需。
B1. 确认沙盒并确定要取消的运行
确认该组织是沙盒(sf org display),并获取要中止的运行的 jobRunId——也就是用户要求取消的运行。同时记录其 DataMaskPolicyId——如果错过中止窗口(B2 退出码 3 / 退出码 1),你需要用它启动替代运行。如果用户刚启动了运行,就使用该 ID;否则查询正在运行的作业:
sf data query --target-org <alias> \
--query "SELECT Id, Status, DataMaskPolicyId FROM DataMaskPolicyJobRun ORDER BY CreatedDate DESC LIMIT 5"
记下所选运行的 DataMaskPolicyId(8dm 前缀)——A3 需要将其作为 <policyId>。
B2. 等待作业进入 running 状态(唯一可中止的状态)
只有当 DataMaskPolicyJobRun.Status 为 running 时,才能中止作业。处于 pending/scheduled 状态的作业会返回 409;终态作业则已完成。使用捆绑的轮询器并以 running 模式轮询,等待进入 running 状态——该模式会在状态读取为 running 时立即退出(默认模式则会等待终态),因此不会错过可中止窗口:
POLL_MODE=running bash scripts/poll-job.sh <alias> <jobRunId> 900 15
超时上限为 900s(15 min),高于约 5–10 min 的调度下限,因此启动较慢的作业仍能被捕获。处理每种退出情况:
- 退出码
0(输出running)→ 直接转到 B3。 - 退出码
3→ 作业在捕获到running状态之前已进入终态;中止窗口已过。针对你在 B1 中记录的策略启动新的运行(使用该<policyId>执行 A3),然后返回此处,轮询新的jobRunId。 - 退出码
1(超时——已达到上限)→ 重新查询作业状态:
如果作业仍非终态(sf data query --target-org <alias> \ --query "SELECT Id, Status FROM DataMaskPolicyJobRun WHERE Id = '<jobRunId>'"pending/scheduled/running),再运行一次轮询器(相同命令)以继续等待。如果状态为running,转到 B3。如果已进入终态,则按退出码 3 处理——启动新的运行(使用 B1 中的<policyId>执行 A3),并轮询新作业。
由于队列等待下限约为 5–10 min,running 窗口通常有几分钟,因此有足够时间捕获该状态;不要不间断轮询。
如果当前没有正在进行的运行(作业已完成,或你必须从头复现完整的“运行→取消”流程),请先使用 A3 启动一个运行,然后返回此处——轮询至其状态为
running,并中止该活动作业。绝不要用较早的、已经进入终态的作业来“演示”取消;中止必须针对实际正在运行的作业。
B3. 通过运行 API 中止
通过运行 API 中止——不要对作业记录执行 DML/删除操作:
sf api request rest \
"/services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort" \
--method POST --body @./empty-body.json --target-org <alias>
如上所述,通过以 @ 为前缀的文件传入空 JSON 请求体({})。200 响应会返回 status: "CANCELED"(大写,来自运行 API)和 message: "Job abort requested"。409 表示作业不处于 running 状态(通常仍为 pending/scheduled)——返回 B2 并继续轮询。
B4. 确认并报告取消结果
取消是异步的。重新查询 DataMaskPolicyJobRun,并确认 Status = canceled(小写,来自 SOQL),再报告中止成功。确认:
- 已确认中止操作针对的是状态查询为
running的活动作业。 - 中止后已重新查询
DataMaskPolicyJobRun,并看到Status = canceled。
高信号规则
| 规则 | 原因 |
|---|---|
每条 sf 命令都要直接运行——绝不能添加任何管道或重定向(|、| python3、| grep、2>&1、2>/dev/null、> file) | sf ... --json 已经会在 stdout 上输出格式干净的 JSON;直接读取即可。重定向或管道会触发无法绕过的 shell 安全防护,导致整个运行悄无声息地停滞,直至超时。切勿使用 python3/grep/jq 做后处理,也不要屏蔽 stderr——即使命令输出了警告,stdout 上的 --json 负载仍然有效;直接按原样解析即可 |
切勿对 DataMaskPolicy* 配置对象使用标准 SOQL / sobject describe | 它们会返回 INVALID_TYPE / NOT_FOUND——请使用 Tooling API 或 MDAPI |
从 DataMaskPolicyJobRunDtl 读取已屏蔽的计数,绝不能臆造 | 子级明细是各对象结果的真实来源 |
只有 completed / completed_with_errors / failed 是终态 | 将 running/scheduled 报告为最终状态是错误的 |
| 只能通过运行 API 的中止端点中止 | 对作业记录执行 DML/删除并不是真正的中止,还会破坏状态 |
中止后务必重新查询状态,并确认状态为 canceled | 中止调用返回 200 并不能证明作业已停止 |
| Data Mask 只能在沙盒上运行 | 生产环境上的运行/中止端点会返回 403 |
使用 API 版本 v67.0 或更高版本,且路径中不能包含 /connect/ 段 | 运行/中止端点路径为 /services/data/v67.0/platform/data-resilience/data-mask/...——路径中包含 connect 段或使用低于 v67 的版本都会返回 NOT_FOUND |
通过 scripts/poll-job.sh 轮询(调用一次),绝不能手动编写 SOQL 循环 | 该脚本会限制尝试次数,并在真实明细行满足条件时提前结束;针对滞后父级状态手动循环,是导致运行超时且没有报告的首要原因 |
注意事项
| 问题 | 解决方法 |
|---|---|
sf sobject describe DataMaskPolicy → NOT_FOUND | 这是 Tooling/MDAPI 实体——使用 --use-tooling-api 查询,不要重试标准 API |
SELECT ... FROM DataMaskPolicy → INVALID_TYPE | 原因相同——对策略使用 Tooling API;标准 API 仅用于 DataMaskPolicyJobRun/Dtl |
运行启动返回 409 | 该策略已有正在进行的运行——轮询现有运行,或等待其完成 |
中止返回 409 "status=PENDING" | 作业仍处于 pending/scheduled 状态,尚未进入 running——继续轮询,只有读取到 running 后才中止;不要放弃中止 |
| 小型作业在你中止前就已完成 | 小型沙盒中的 running 窗口只有几秒——启动一次新的运行并密集轮询;绝不能用之前已中止的作业冒充整个流程 |
中止返回 200,但 SOQL 状态仍为 running | 取消是异步的——继续轮询 SOQL 状态,直到变为 canceled;不要过早报告成功 |
运行 API 显示 CANCELED,但 SOQL 显示 running | 大小写和数据表面不同:运行 API 使用大写,SOQL 选项列表值为小写。判断终态时以 SOQL 值为准 |
| 作业瞬间“完成” | 重新检查:scheduled 不是终态。轮询,直到实际出现终态值 |
运行/中止端点返回 NOT_FOUND | 路径必须是 /services/data/v67.0/platform/data-resilience/data-mask/...——不能包含 /connect/ 段,且版本须为 v67.0 或更高(Core 262)。请参阅 references/api-surface.md |
输出格式
仅报告你运行的工作流对应的章节,不要为另一个工作流添加章节。**保持精简:每条命令只在所属步骤展示一次;不要在末尾附加一份重复调用记录的“完整命令日志”。**优先使用精简表格,而非段落;读者应能在首屏看到关键结果。
工作流 A(脱敏并报告):
- 使用的策略(Id 和标签,以及是复用还是新建)
- 运行情况 — job Id、最终终态、脱敏记录数(来自详情对象)。将轮询循环压缩为一行(例如:“轮询 5 次,
running→completed”);不要逐次列出轮询结果。 - 逐对象结果 — 来自
DataMaskPolicyJobRunDtl。报告各行实际包含的对象级计数;如果没有字段级错误行,应以推断的方式说明(“未报告字段级错误”),不要声称每个字段都已成功。准确措辞见 A5。 - 运行的命令 — 上文已内联展示;此处只列出尚未展示的命令。不要再次粘贴完整命令序列。
工作流 B(取消运行):
- 已取消的任务 — job Id、取消时的状态为
running、通过运行 API 的中止端点发出中止请求,以及重新查询后得到的canceled状态。 - 运行的命令 — 同上,不要再次粘贴。
提高事实准确性的注意事项:
- 运行/中止 REST 响应返回 15 位
jobRunId(例如1aGXK0000000uob);SOQL 返回的是同一条记录的 18 位形式(例如1aGXK0000000uob2AA)。它们是同一个任务——两者同时出现时,请注明这一点,不要将它们写成两个 Id。 - 不要声称实际未查询的脱敏数量、字段级结果或终态。报告中的每个数字都必须能追溯到命令日志中展示的查询结果。
跨技能集成
| 需求 | 委派给 | 原因 |
|---|---|---|
| 创建真实的 PII 记录以供脱敏 | platform-data-manage | 测试数据创建 |
| 编写自定义匿名化 Apex | platform-apex-generate | Apex 编写 |
| 将策略元数据部署到组织 | platform-metadata-deploy | 元数据部署 |
参考文件索引
| 文件 | 阅读时机 |
|---|---|
references/api-surface.md | 各实体 API 的确切范围、所有 CLI 命令、运行/中止 REST 端点以及状态选项值 |
references/policy-authoring.md | 两步编写流程(MDAPI 精简框架 → Tooling 对象/字段插入)以及 MaskingCategory/MaskValue 处理表 |
references/run-and-abort.md | 完整的运行 → 轮询 → 报告 → 重新运行 → 中止流程,含示例响应 |
scripts/poll-job.sh | 有界轮询器:等待终态(默认),或设置 POLL_MODE=running 等待可中止的 running 窗口 |