mcpField
P
platform-custom-metadata-type-generate

platform-custom-metadata-type-generate

github.com/forcedotcom开发1.1k1.4k 次安装1 天前更新
代码仓库

关于 platform-custom-metadata-type-generate

用于生成和校验 Salesforce 自定义元数据类型(CMDT)、字段及记录的技能,面向维护可跨组织部署配置的开发者。它涵盖元数据关系字段和可部署的 `.md-meta.xml` 记录文件。

主要功能

  1. 01生成 CMDT 类型定义与字段
  2. 02校验支持的字段类型和关系
  3. 03生成可部署的 CMDT 记录文件
  4. 04按字段类型输出记录值格式
  5. 05诊断部署限制与常见错误

适用场景

  1. 01创建随软件包部署的组织配置
  2. 02从 CSV 或列表批量生成 CMDT 记录
  3. 03排查元数据关系或记录部署问题

安装

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

$npx skills add https://github.com/forcedotcom/sf-skills --skill platform-custom-metadata-type-generate

安装后在 Claude Code 中输入/platform-custom-metadata-type-generate确认已加载。

附带文件references/cmdt-records.mdscripts/sanitize-developer-name.sh

详细介绍

Salesforce 自定义元数据类型生成器和验证器

何时使用此技能

需要以下操作时,请使用此技能:

  • 创建自定义元数据类型(__mdt)
  • 生成或验证 CMDT 字段,包括 MetadataRelationship 字段
  • 将 CMDT 记录生成为可部署的 .md-meta.xml 文件
  • 排查 CMDT 部署错误

如需了解此技能未涵盖的权威 Metadata API 类型、字段和枚举详情,请使用 platform-metadata-api-context-get 技能。

1. 概述与用途

自定义元数据类型会生成两种独立的构件系列,大多数请求都需要两者:

构件路径元数据类型
类型定义objects/<Name>__mdt/<Name>__mdt.object-meta.xmlCustomObject
字段objects/<Name>__mdt/fields/<Field>__c.field-meta.xmlCustomField
记录customMetadata/<Name>.<Record>.md-meta.xmlCustomMetadata

**API 名称后缀:**类型以 __mdt 结尾;字段仍以 __c 结尾。

**相较于自定义设置,CMDT 的核心优势在于:CMDT 记录属于元数据,因此可以在不同组织之间部署。**如果用户希望配置“随软件包一起发布”或“在每个组织中都相同”,CMDT 就是正确选择。

根元素是 <CustomObject>,但自定义对象的大多数规则几乎都不适用。常规自定义对象通常需要或会使用 sharingModel、nameField 和 deploymentStatus,但在此处它们会导致严重错误。不要沿用 platform-custom-object-generate 中的假设。


2. 语法要点 — 类型定义(第 1 层)

必需和允许的元素
元素要求说明
<label>必需UI 中显示的单数名称
<pluralLabel>必需省略时会报错 Must specify a non-empty plural label for the CustomObject
<visibility>始终包含使用 Public;仅在开发/沙盒/临时组织中使用 Protected/PackageProtected(第 6 节)
<description>始终包含说明此类型配置的内容及其所有者

<pluralLabel> 在此处必需、但在自定义设置中禁止,是这两类对象之间最常被弄反的规则。任一错误都不会提及另一类对象的规则。

禁止的元素

以下每个元素都会导致错误 Cannot specify: <element> for Custom Metadata Type。重用常规自定义对象的骨架(包含 sharingModel、deploymentStatus、nameField 块或 enableSearch)通常是导致此错误的原因:

sharingModel、nameField、deploymentStatus、enableActivities、enableReports、enableHistory、enableSearch

正确 — 最精简的有效 __mdt 类型:

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <label>Partner Tier</label>
  <pluralLabel>Partner Tiers</pluralLabel>
  <description>Discount and threshold configuration per partner tier. Ships with the package; edited by the revenue ops team.</description>
  <visibility>Public</visibility>
</CustomObject>

3. 字段规则

此技能仅涵盖 CMDT 特有的差异 — 以下类型允许列表、fieldManageability 和 MetadataRelationship。通用字段机制(<fullName> 的推导、<label>、<description>、<inlineHelpText>、精度/小数位数、<length>、visibleLines)请参阅 platform-custom-field-generate。

支持的字段类型

Checkbox、Date、DateTime、Email、Number、Percent、Phone、Picklist、Text、TextArea、LongTextArea、Url,以及 MetadataRelationship。

不支持的字段类型

Currency、AutoNumber、MasterDetail、Summary、Location、Time、EncryptedText、Html、MultiselectPicklist。每种类型都会产生格式明确且精确的错误:

Type {TypeName} of CustomMetadataField {Object}__mdt.{Field}__c is not supported for the Entity {Object}__mdt

(末尾没有句点。)例如:Type Currency of CustomMetadataField Partner_Tier__mdt.Discount__c is not supported for the Entity Partner_Tier__mdt

Currency 是最常见的陷阱 — 它在自定义设置中可用,但在此处不可用。请改用带有 <precision>/<scale> 的 Number,并将货币放在标签或帮助文本中。

公式字段不受支持,但它们不遵循上述模式。在其他方面合法的类型上添加 <formula> 元素时,只会返回以下错误:

Invalid data type.
Lookup 会被静默强制转换 — 严重

当 referenceTo 解析为真实 sObject 时,__mdt 上的 <type>Lookup</type> 不会报错:部署成功,平台会静默地将字段改写为 MetadataRelationship。之后检索时会显示用户从未写入过的字段:

<type>MetadataRelationship</type>   

始终显式写入 MetadataRelationship。 使用 Lookup 会导致部署成功,但源文件与组织之间不匹配;任何人第一次检索时,这种差异都会造成 Git 变更。(已在实时部署中复现:可解析的 Lookup 会返回为 MetadataRelationship;无法解析的 referenceTo 会被正常拒绝。)

MetadataRelationship

有三种有效的目标,由 referenceTo 选择。

<referenceTo>用途额外要求
另一个 __mdt 类型关联两个自定义元数据类型必须是不同的类型
EntityDefinition指向一个 sObject无
FieldDefinition指向一个字段需要 <metadataRelationshipControllingField>
<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Target_Field__c</fullName>
  <label>Target Field</label>
  <type>MetadataRelationship</type>
  <referenceTo>FieldDefinition</referenceTo>
  <metadataRelationshipControllingField>Partner_Tier__mdt.Target_Object__c</metadataRelationshipControllingField>
  <relationshipLabel>Target Field</relationshipLabel>
  <relationshipName>Target_Field</relationshipName>
</CustomField>

控制字段必须是同一类型上的 EntityDefinition 关系,引用格式为 Type__mdt.Field__c。省略该字段会报错:Metadata relationships to Field Definition require a controlling field.

不可能自引用。 将 MetadataRelationship 指向其自身的父类型会失败,并报错 Cannot add a self-lookup relationship child with cascade or restrict options to the object itself(这是从实时 __mdt 部署中逐字摘录的错误信息)——请使用第二种类型。

<fieldManageability>

可选,默认值为 DeveloperControlled——除非用户需要其他值,否则不要添加。有效值为 DeveloperControlled、SubscriberControlled 和 Locked。

它仅适用于 __mdt 字段。在常规自定义对象字段或自定义设置字段上使用时:

Field manageability cannot be set on this entity.

4. CMDT 记录(.md-meta.xml)

文件名约定

写入 customMetadata/<TypeNameWithout__mdt>.<RecordDeveloperName>.md-meta.xml。

customMetadata/Partner_Tier.Bronze_AMER.md-meta.xml

文件名中带有 __mdt 后缀也能部署并创建真实记录,因此可以接受——但 Salesforce 检索时会将其规范化为不带后缀的格式,所以应始终省略 __mdt,以避免 Git 变更(详见 references/cmdt-records.md)。

必需的命名空间

根元素上必须包含以下三个命名空间——没有它们,值中的 xsi:type 将无法解析。

<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
xsi:type 映射 — 每个值都需要一个
字段类型正确的 xsi:type示例值
复选框xsd:booleantrue
日期xsd:date2024-01-15
日期时间xsd:dateTime2024-01-15T10:30:00.000Z
数字、百分比xsd:double42.0
文本、文本区域、长文本区域xsd:stringsome text
电子邮件、电话、Urlxsd:string[email protected]
选项列表xsd:stringBeta
MetadataRelationship → EntityDefinitionxsd:stringAccount
MetadataRelationship → FieldDefinitionxsd:stringAccount.Name
任意类型,空值不写 xsi:type — 写入 <value xsi:nil="true"/>

通过 MetadataRelationship 指向另一个 __mdt 类型的值尚未验证——预期应使用 xsd:string 和目标的 DeveloperName,但请确认。

不得输出 xsd:picklist — 严重

Salesforce 自身的文档建议在选项列表字段中使用 xsd:picklist。这是错误的——它不是有效的 XML Schema 类型。平台不会返回明确的验证错误,而是直接在服务端使整个部署失败,不提供组件级诊断,并连带使其他所有组件都无法部署。选项列表字段的值始终使用 xsd:string;绝不要使用 xsd:picklist。 参见 references/cmdt-records.md §2。

记录正文规则
  • <label> 是必填项。省略该字段会报错 Required fields are missing: [MasterLabel]——注意,错误中报告的是 sObject 字段名 MasterLabel,而不是 label。
  • <protected> 是可选项,默认值为 false。
  • 切勿在 <values> 块中放入 <fullName>——这会导致硬解析错误。
  • <value xsi:nil="true"/> 可用于可选字段。用于必填字段时,会被拒绝,效果与字段缺失完全相同。非空值若省略 xsi:type,始终会导致致命错误,包括文本字段。

正确示例——完整记录:

<?xml version="1.0" encoding="UTF-8"?>
<CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <label>Bronze AMER</label>
  <protected>false</protected>
  <values>
    <field>Discount_Percent__c</field>
    <value xsi:type="xsd:double">5.0</value>
  </values>
  <values>
    <field>Region__c</field>
    <value xsi:type="xsd:string">AMER</value>
  </values>
  <values>
    <field>Effective_Date__c</field>
    <value xsi:type="xsd:date">2024-01-15</value>
  </values>
  <values>
    <field>Notes__c</field>
    <value xsi:nil="true"/>
  </values>
</CustomMetadata>
记录 DeveloperName 规则

名称是文件名的第二段。名称必须以字母开头,只能包含字母数字字符和下划线,不能以下划线结尾,不能包含连续的两个下划线,且最多为 40 个字符。违反规则会报错:

Custom Metadata Record Name: The <Type>__mdt API Name can only contain underscores and alphanumeric characters. It must be unique, begin with a letter, not include spaces, not end with an underscore, and not contain two consecutive underscores.

超过 40 个字符时会报错 Value too long for field: fullName maximum length is:40(冒号后没有空格)。

部署顺序

没有顺序限制——只要类型和所有引用的字段能够解析即可(已存在于组织中,或包含在同一次部署中)。两种错误会告知缺少的内容: Custom metadata type <Type>__mdt is not available in this organization.(类型缺失)以及 <Type>__mdt: could not find fields: <Field>__c(类型存在,但字段不存在)。

→ 完整的记录值错误目录和 xsi:type 强制转换差异:references/cmdt-records.md。

根据用户输入生成记录

记录数据有三种形式;每种情况下都要为每一行生成一个 <Type>.<Record>.md-meta.xml 文件,将列映射为 <field>/<value> 对,并使用正确的 xsi:type:

  • 内联列表/表格——将列直接映射到字段。
  • CSV——标题行使用字段 API 名称(或将标签映射到字段 API 名称),每行对应一条记录;从键列获取 DeveloperName。
  • 散文——推断各行(“Bronze 为 5%,Gold 为 15%”),并在生成前确认字段映射。

必须从键列或标签确定性地派生每个 DeveloperName——切勿凭空编写。对于每一行,在写入其记录文件之前运行 scripts/sanitize-developer-name.sh "<LABEL>" <ROW>(相同输入 → 相同的有效名称;空标签、全符号标签或非拉丁字符标签会回退为 Record_<ROW>)。

该转换是多对一的——United States 和 United-States 都会得到 United_States;由于每条记录的文件名都是 <Type>.<DeveloperName>.md-meta.xml,发生冲突时会静默覆盖其中一行。派生出整个批次的名称后,先检查重复项,再写入文件;如有冲突,停止并报告发生冲突的源标签,不要覆盖(只有用户要求自动解决时,才追加确定性的 _2/_3 后缀)。有序算法、仅支持 ASCII 的限制、唯一性检查及示例:references/cmdt-records.md §7。

在 Apex 中使用记录

通过生成的强类型类读取记录——不使用 SOQL,也不消耗查询行数,可安全用于循环和触发器:

Map<String, Partner_Tier__mdt> all = Partner_Tier__mdt.getAll();
Partner_Tier__mdt bronze = Partner_Tier__mdt.getInstance('Bronze_AMER');  // by DeveloperName

访问器截断——对于 LongTextArea 至关重要。 getAll()/getInstance() 对任何字段都只返回前 255 个字符;对于这些调用方,较长的 LongTextArea 值会被静默截断,必须通过 SOQL 读取(SELECT ... FROM Partner_Tier__mdt)。在 CMDT 上添加 LongTextArea 时,务必说明这一点。


5. 选择合适的组件

生成之前,请确认用户需要的是 CMDT。

如果配置是……使用原因
必须随记录一起部署到不同组织的参考数据CMDT(此技能)记录属于元数据
由管理员维护的映射/查找/交叉映射表(字段↔字段、代码↔代码、“将 A 映射到 B”)CMDT(此技能)可编辑的参考数据,每对值一条记录
可按配置文件/用户编辑,或仅限组织本地platform-custom-setting-generate值属于数据,并且只保留在一个组织中
用户在运行时创建和编辑的业务记录platform-custom-object-generateCMDT 记录不是事务数据
可翻译的 UI 文本自定义标签 — 此处不生成标签是用于翻译的对象
凭据、API 密钥、令牌命名凭据/外部凭据 — 此处不生成参见第 6 节
Apex 或流程中的权限检查自定义权限 — 此处不生成布尔访问权限应属于权限模型

最重要的区别是:CMDT 记录会部署,自定义设置值不会。 如果用户需要管理员在运行时按配置文件编辑值,那么应该使用自定义设置,而不是 CMDT。

字段映射和查找表是典型的 CMDT 用例。 当用户要求“将字段从 A 映射到 B”、维护代码到代码的查找关系,或保留可由管理员编辑的交叉映射表时,应将其建模为 CMDT — 每对值一条记录(例如 Lead_Field__c/Target_Field__c,或 MetadataRelationship 字段到 FieldDefinition)— 绝不要使用硬编码的 Apex Map、常量或流程决策。措辞中提到“用于转换”或“用于我们的集成”,并不意味着它属于代码:如果管理员无需部署即可维护这些对应关系,它就是 CMDT 参考数据。


6. 可见性、机密信息与禁止静默降级规则

Protected 和 PackageProtected 取决于组织类型

这两种设置只能部署到开发组织、沙盒组织或临时组织。在其他任何组织中:

You can't set the visibility for a Custom Metadata Type to Protected unless you are in a developer, sandbox, or scratch org.

将 PackageProtected 替换为 Protected,即可得到相同的字符串。此错误已在类似生产环境的组织中得到确认;这类值在开发组织、沙盒组织或临时组织中可以成功,这一点来自消息文本,而非单独测试的结果。

禁止静默降级规则 — 关键

如果 Protected 因组织类型而失败,绝不要通过切换为 Public 来“修复”。 请停止并告知用户。静默降级会把有意的保密选择变成全世界可读的组件,同时部署成功且没有任何警告 — 这是最糟糕的结果,因为没有任何信号表明发生了变化。

应报告实际情况:该组织不允许使用 Protected,因此可以部署到开发组织/沙盒组织/临时组织,或接受使用 Public。由用户选择。

此规则适用于任何缩小可见性的情况,不仅限于此错误。

CMDT 不适合存储机密信息

CMDT 不是机密信息存储库。Protected 会限制命名空间外部的访问,但它不是加密,也不能保护该值不被命名空间内部的代码或管理员访问。

当用户要求将 API 密钥、密码、令牌、客户端机密或证书存储在 CMDT 中时:

  1. 先明确警告 — 在生成任何内容之前 — CMDT 不适合存放凭据,并且 Protected 不是加密。
  2. 推荐合适的组件 — 命名凭据搭配外部凭据。明确说明此技能不会生成这些组件。
  3. 如果用户仍坚持,则照办 — 在组织允许的情况下,以 Protected 可见性生成 CMDT,并在回复中保留警告。不要默默拒绝,也不要默默照办。

两条规则需结合适用,且先后顺序很重要。 如果用户坚持用 CMDT 存储机密信息,同时组织拒绝 Protected,那么禁止静默降级规则可以防止密钥落入 Public 组件。请停止并报告 — 绝不要将包含机密信息的类型降级为 Public。


7. 部署错误参考

第 2–4 节在每种类型和字段级错误字符串旁边,都注明了导致该错误的操作(Cannot specify: <element> for Custom Metadata Type、不支持的类型字符串、公式的 Invalid data type.、Field manageability cannot be set on this entity.、MetadataRelationship 的控制字段和自查找错误,以及 Must specify a non-empty plural label)。

完整原文记录值目录——包括每种错误 xsi:type 的拒绝信息、Required fields are missing: [MasterLabel]、必填字段上的 nil、DeveloperName 规则,以及精确文本陷阱(字段名使用 Label 而非 API 名称、无 xsi:type 错误中的双空格和 i.g. 拼写错误,以及没有空格的 maximum length is:40)——均见 references/cmdt-records.md。

请注意一个对应关系:此处的后缀是 for Custom Metadata Type(有空格,标题大小写);自定义设置使用 for CustomSettings。匹配这些字符串时,不要假定它们共用同一模板。


8. 验证清单

类型定义检查
  • 是否都包含 <label>、<pluralLabel> 和 <visibility>?
  • 是否包含具体明确的 <description>?
  • 是否未包含 <sharingModel>、<nameField> 和 <deploymentStatus>?
  • 是否未包含 <enableActivities>、<enableReports>、<enableHistory> 和 <enableSearch>?
  • 目录和文件名是否以 __mdt 结尾?
字段检查
  • 每种字段类型是否都在支持列表中(第 3 节)?
  • 是否未包含 Currency?(此处不支持——请使用 Number)
  • 是否未包含任何 <formula> 元素?
  • 是否未包含 <type>Lookup</type>,并改用 MetadataRelationship?关键——Lookup 会静默部署并被重写
  • 对于 FieldDefinition 关系,是否包含 <metadataRelationshipControllingField>?
  • 是否没有任何 MetadataRelationship 指向其自身的父类型?
  • 是否仅在用户要求非默认值时才填写 <fieldManageability>?
记录检查 关键
  • 文件名是否为 <Type>.<Record>.md-meta.xml,且不含 __mdt?
  • 根元素上是否包含全部三个命名空间(xmlns、xmlns:xsd、xmlns:xsi)?
  • 每个非 nil 的 <value> 是否都带有 xsi:type?
  • 是否完全未使用 xsi:type="xsd:picklist"?(这会导致整个部署崩溃——请使用 xsd:string)
  • 每个 xsi:type 是否都与第 4 节表格中的字段类型匹配?
  • 每条记录是否都包含 <label>?
  • 每个 <values> 块中是否都未包含 <fullName>?
  • 每个必填字段是否都设置了实际值,而非 xsi:nil="true"?
  • 每条记录的 DeveloperName 是否不超过 40 个字符、以字母开头,且不含空格或连字符?
  • 如果记录来自批次、列表或 CSV,所有派生的 DeveloperName 是否都互不相同(使用 sort | uniq -d 检查),并且是否报告了任何冲突,而非静默覆盖行?关键——冲突会导致记录丢失,且不会出现部署错误
  • 每个 <field> 是否都指定了该类型中已有或此次部署中包含的字段?
  • 如果记录来自列表、CSV 或文字描述,是否每行恰好生成一个文件,并将各列映射到正确字段?
使用检查
  • 如果用户询问如何读取记录,是否提供了使用 getAll() / getInstance(developerName) 的 Apex 代码片段?
  • 如果有记录填入 LongTextArea,是否提醒用户访问器会将内容截断为 255 个字符,且要获取完整值必须使用 SOQL?
安全检查 关键
  • 如果请求涉及机密、密钥、令牌或密码,是否在生成内容之前发出警告,并建议使用 Named Credential?
  • 是否保留了 Protected,而未在组织类型不匹配时静默降级为 Public?
组件选择检查
  • 如果管理员需要在运行时按配置文件或用户编辑值,是否建议改用 platform-custom-setting-generate?
  • 如果这些数据实际上是事务性业务记录,是否建议改用 platform-custom-object-generate?

参考文件索引

文件阅读时机
references/cmdt-records.md调试 CMDT 记录部署时——完整的逐字记录值错误目录、类型强制转换的不对称性(平台会静默接受哪些 xsi:type 不匹配),以及经过演练的记录示例

评论

支持 Markdown