关于 platform-salesforce-connect-adapter-generate
用于 Salesforce Connect 的开发技能,可指导构建和部署自定义 Apex 适配器。它会先收集 API 信息,再生成两个 Apex 类、元数据并提供设置步骤。
主要功能
- 01生成 Connection 和 Provider Apex 类
- 02将 REST 响应映射为外部对象架构
- 03支持只读和读写适配器
- 04通过 sf CLI 部署类
- 05指导手动设置与验证
适用场景
- 01将 REST API 数据作为 Salesforce 记录访问
- 02构建需认证或公开的自定义适配器
- 03配置分页式外部数据访问
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-salesforce-connect-adapter-generate安装后在 Claude Code 中输入/platform-salesforce-connect-adapter-generate确认已加载。
附带文件references/official-examples.mdreferences/scenarios.md
详细介绍
Salesforce Connect — 自定义 Apex 适配器
引导用户构建完整的自定义 Salesforce Connect Apex 适配器:两个 Apex 类、元数据部署和注册。不要猜测用户的 API 结构——生成前先询问。
范围
**范围内:**生成 DataSource.Connection 和 DataSource.Provider Apex 类、Named Credential 元数据、通过 sf CLI 部署,以及针对任意 REST API 提供分步的 Setup 注册指南。
**范围外:**配置标准适配器(OData、Snowflake、GraphQL——它们有各自的流程)。生成 External Data Source 元数据文件(无法通过 sf CLI 部署——必须在 Setup 中手动注册)。编写通过 Salesforce Connect 框架以外的方式调用外部 API 的 Apex(请使用 platform-apex-generate 或 integration-connectivity-generate 技能)。
开始之前
确认两件事。如果缺少任何一项,请先询问再继续。
1. 这确实是 Salesforce Connect 用例吗?
| 用户想要…… | 正确的工具 |
|---|---|
| 实时查询外部数据而不复制数据——只读或读写,并以 Salesforce 记录的形式显示 | 此技能 |
| 按计划将数据复制或同步到 Salesforce | platform-data-manage 技能 |
| 连接到 OData、GraphQL、DynamoDB、Athena 或 Cross-Org | 标准适配器设置——不需要 Apex,使用不同的流程 |
| 通过 Salesforce 原生 Snowflake 适配器连接 Snowflake(直接使用 Snowflake 协议) | 内置 Snowflake 适配器——不需要 Apex |
| 通过 REST 或 HTTP API 访问 Snowflake(或任何数据库)数据 | 此技能——Snowflake REST 端点 → 自定义 Apex 适配器 |
| 从 Apex 或 Flow 逻辑调用外部 API | platform-apex-generate 技能或 integration-connectivity-generate |
| 向外部系统提供 Salesforce 数据 | integration-connectivity-generate |
| 接收实时推送的数据或订阅外部事件 | 这不是 Salesforce Connect——Connect 仅支持拉取。请改用 Platform Events 或 Change Data Capture |
| 为分析或批量处理同步或复制数据 | Data Cloud 或 ETL——Connect 仅用于零复制虚拟化 |
如果用户有可用的标准适配器,请告知他们——在标准适配器适用时编写自定义适配器是不必要的工作。
2. 他们有 Salesforce Connect 许可证吗?
自定义适配器需要 Salesforce Connect 许可证(每个 External Data Source 需要一个许可证)。没有许可证时,External Data Source 菜单不会显示 Apex 选项。如果用户没有许可证,请在继续之前告知他们。
默认组织上下文(本地运行时)
如果用户未指定组织别名,请使用 demo-org。
如果用户未指定 SFDX 项目路径,请使用 ~/salesforce-connect-apex-skill/sfconnect-demo/。
这些是本地测试的默认值——如果用户指定了自己的值,始终以用户指定的值为准。
生成前收集输入
缺少以下任何信息时,都不得生成代码;请先询问:
| 输入 | 重要原因 |
|---|---|
| API 名称及其功能 | 用于命名类,并为字段映射提供上下文 |
| 基础 URL | 将成为 Named Credential 端点 |
| 身份验证类型 | 决定 Named Credential 设置和 getAuthenticationMode() |
| 关键 GET 端点 + 示例响应 | 定义 External Object 架构——字段名称、类型和嵌套结构 |
| 是否需要写入支持? | 决定是否实现 upsertRows() 和 deleteRows() |
| 组织类型(DE、scratch、sandbox) | 设置 .cls-meta.xml 中的 apiVersion |
如果用户提供了 OpenAPI 规范,请提取:
- 所有返回数组的 GET 端点 → 每个端点对应一个
DataSource.Table - 响应对象属性 →
DataSource.Column条目 id/uuid/ 主键 → 映射到ExternalId- 属性类型 → 使用下方的字段类型映射表
要构建的内容
每个自定义 Salesforce Connect 适配器都恰好由两个 Apex 类组成。
DataSource.Connection
负责与外部 API 通信。具体签名如下:
override global DataSource.TableResult query(DataSource.QueryContext context)
override global List<DataSource.TableResult> search(DataSource.SearchContext context)
// Write support — only if API supports it:
global override List<DataSource.UpsertResult> upsertRows(DataSource.UpsertContext context)
global override List<DataSource.DeleteResult> deleteRows(DataSource.DeleteContext context)
关键区别:query() 始终只针对一个表操作(QueryContext 只有一个 TableSelection)。search() 可以同时针对多个表操作(SearchContext 有多个 TableSelection 实例)——在循环中逐个处理,并为每个表返回一个结果。
DataSource.Provider
向 Salesforce 声明适配器的功能和架构。必需的方法:
getAuthenticationMode()— 公共 API 返回ANONYMOUS;需要身份验证的 API 返回NAMED_PRINCIPAL或PER_USERgetCapabilities()— 声明QUERY、SEARCH;仅在实现写入功能时添加ROW_CREATE、ROW_UPDATE、ROW_DELETEgetConnection(ConnectionParams params)— 返回new YourConnection(params)sync()— 在用户于 Setup 中点击“Validate and Sync”时调用;返回用于定义 External Object 架构和列的List<DataSource.Table>
Provider 类会在 Salesforce Setup 的 External Data Source 类型下拉菜单中显示为 Custom-[ClassName]。
**重要:**每次编辑 Connection 类后,都必须再次保存 Provider 类,即使没有更改也一样。否则,适配器会从 Type 选择列表中消失,现有的 External Object 选项卡也会失效。
字段类型映射
| 外部 API 类型 | 使用此类型 |
|---|---|
| string / text | DataSource.DataType.TEXT_TYPE |
| number / integer / decimal | DataSource.DataType.NUMBER_TYPE |
| boolean | DataSource.DataType.BOOLEAN_TYPE |
| date (ISO 8601) | DataSource.DataType.DATE_TYPE |
| datetime / timestamp | DataSource.DataType.DATETIME_TYPE |
| URL | DataSource.DataType.URL_TYPE |
DataSource.DataType.EMAIL_TYPE | |
| phone | DataSource.DataType.PHONE_TYPE |
| enum / picklist | TEXT_TYPE — 将枚举值映射为字符串 |
| nested object / JSON blob | 展平为标量字段,或使用 TEXT_TYPE 并在 query() 中解析 |
| array | 派生一个计数字段(NUMBER_TYPE),或展平首个元素的字段 |
| string > 255 chars | 长文本区域 — 不要截断;Salesforce 会自动映射 |
每个 DataSource.Table 必须包含以下列:
DataSource.Column.text('ExternalId', 255) // unique key from external system — REQUIRED
DataSource.Column.text('Name', 255) // display label shown in Salesforce UI — REQUIRED
DataSource.Column.url('DisplayUrl') // link to the record in the external system — recommended
缺少 ExternalId 是适配器成功部署但记录未显示的最常见原因。
DisplayUrl 可在列表视图中启用可点击的链接图标 — 请填入外部记录的 URL。
场景
共有四种场景:(1) 公共只读,(2) API 密钥/OAuth 身份验证,(3) 支持 upsert/delete 的读写操作,(4) 分页 API。每种场景的完整代码示例见 references/scenarios.md。
各场景的主要区别:
| 场景 | 身份验证模式 | 额外功能 | Named Credential |
|---|---|---|---|
| 公共只读 | ANONYMOUS | QUERY、SEARCH | 作为元数据部署 |
| 需要身份验证 | NAMED_PRINCIPAL 或 PER_USER | QUERY、SEARCH | 在 Setup 中手动创建 — 切勿将凭据作为元数据部署 |
| 读写 | ANONYMOUS 或身份验证 | 添加 ROW_CREATE、ROW_UPDATE、ROW_DELETE | 同上 |
| 分页 | 任意 | 任意 | 使用 context.tableSelection.numberOfRows(若为 null,默认值为 500);不会自动调用 queryMore |
如何验证适配器是否正常工作
选项 1 — 通过 sf CLI 使用 SOQL(最快,无需 UI)
sf data query \
--query "SELECT ExternalId, Name__c FROM [YourObject]__x LIMIT 5" \
--target-org [org-alias]
如果返回了行,说明适配器正常工作。如果返回 0 行,请检查以下故障排查步骤。
选项 2 — Visualforce 页面(演示效果最佳,适用于任何 org)
部署一个 Visualforce 页面和 Apex 控制器,查询 [Object]__x 并在 apex:pageBlockTable 中呈现行。通过 [orgUrl]/apex/[PageName] 打开。测试时比列表视图更可靠 — 可绕过选项卡要求、部署状态和筛选器问题。标准模板见 references/scenarios.md。
选项 3 — 列表视图(标准界面,但需要额外步骤)
要让列表视图显示记录,以下四项必须全部满足:
- 外部对象部署状态 = 已部署(对象管理器 → 编辑)
- 外部对象已创建选项卡(设置 → 选项卡 → 自定义对象选项卡 → 新建)
- 列表视图筛选器设为 全部(而非“最近查看”)
- 外部 API URL 已配置远程站点设置
故障排查:无错误但记录数为 0
| 症状 | 原因 | 修复方法 |
|---|---|---|
| SOQL 返回 0 条记录 | 缺少远程站点设置 | 设置 → 远程站点设置 → 新建 |
| SOQL 返回 0 条记录 | 未找到命名凭证 | 设置 → 命名凭证 → 确认 callout: 名称完全匹配 |
| SOQL 返回 0 条记录 | 组织代理阻止了外部 URL | 使用回环模式,或切换到外部 DE 组织 |
| 列表视图显示 0 条记录 | 部署状态 = 开发中 | 对象管理器 → [Object]__x → 编辑 → 已部署 |
| 列表视图显示 0 条记录 | 筛选器为“最近查看” | 将筛选器改为“全部” |
| 应用程序启动器中没有显示任何内容 | 尚未创建选项卡 | 设置 → 选项卡 → 自定义对象选项卡 → 新建 |
Agent 部署的内容与开发者手动操作的内容
Agent 部署(执行一条 sf project deploy start 命令)
| 文件 | 说明 |
|---|---|
classes/[API]DataSourceConnection.cls + 元数据 | 始终需要 |
classes/[API]DataSourceProvider.cls + 元数据 | 始终需要 |
namedCredentials/[API].namedCredential-meta.xml | 仅限公共 API — 需要凭证的 API 均跳过 |
不要生成 externalDataSources/[API].externalDataSource-meta.xml。 部署 Apex 类后,必须在设置中手动注册外部数据源。sf project deploy start 命令无法部署自定义 Apex 外部数据源 — sf CLI 会报错:“Could not infer a metadata type.” 告诉用户部署后前往设置 → 外部数据源 → 新建。
正确的 NamedCredential XML 格式(没有 <name> 元素 — API 名称取自文件名):
<?xml version="1.0" encoding="UTF-8"?>
<NamedCredential xmlns="http://soap.sforce.com/2006/04/metadata">
<label>My API</label>
<endpoint>https://api.example.com</endpoint>
<allowMergeFieldsInBody>false</allowMergeFieldsInBody>
<allowMergeFieldsInHeader>false</allowMergeFieldsInHeader>
<generateAuthorizationHeader>false</generateAuthorizationHeader>
<principalType>Anonymous</principalType>
<protocol>NoAuthentication</protocol>
</NamedCredential>
部署命令:
sf project deploy start \
--source-dir force-app/main/default/ \
--target-org [org-alias]
开发者手动操作(始终需要 — 不存在对应的元数据)
验证并同步会实时调用外部 API,以发现其架构并创建外部对象定义。此操作无法通过脚本完成。
Setup → External Data Sources → [your data source] → Validate and Sync
Check the box next to each External Object → Sync
App Launcher → search [External Object name] → confirm records appear
结尾始终使用:“部署完成。还差一步:在 Salesforce 设置中打开外部数据源,然后点击‘验证并同步’。”
错误处理规则
绝不能让异常未经捕获就从 query() 中传播出去 — Salesforce 会向用户显示没有上下文信息的通用错误。
| 情况 | 处理方式 |
|---|---|
| HTTP 401 | throw new DataSource.OAuthTokenExpiredException() |
| HTTP 429 / 速率限制 | 返回空行;通过 System.debug() 记录日志 |
| HTTP 500 或网络错误 | 返回空行;不要重新抛出异常 |
| 响应中的字段为 null | record.get('field') != null ? (String) record.get('field') : '' |
| 无法扁平化的嵌套 JSON | 将原始 JSON 字符串作为 TEXT_TYPE 字段返回,而不是让整个查询失败 |
管理器限制
- 每个事务最多 100 次 HTTP callout — 每次
query()调用都对应一个事务 - 堆内存限制为 6 MB(同步)— 不要反序列化超大负载;使用服务端分页
- CPU 限制为 10 秒 — 避免对大型响应数组使用嵌套循环
设置前提条件 — 严格按此顺序操作
1. 先部署 Apex 类
只有在组织中部署了至少一个 DataSource.Provider 子类后,外部数据源类型下拉列表中才会显示 Salesforce Connect: Custom (Developed with Apex) 类型。
这不是权限问题,而是依赖关系——Salesforce 会在运行时发现可用的 Provider 类。没有已部署的 Provider,下拉列表中就不会显示该选项。
正确顺序——务必遵循:
- 先部署 Apex 类(
sf project deploy start) - 然后前往 Setup → External Data Sources → New → 此时会显示 Custom Apex 类型
如果用户说“我在下拉列表中看不到 Custom Apex 选项”,答案是:先部署这些类,然后再返回 Setup。
2. 测试前添加 Remote Site Setting
必须将外部 API 的基本 URL 加入白名单,否则调用会静默失败——query() 会返回空行,不会抛出异常,也不会显示错误消息。可将其作为元数据部署,或前往 Setup → Remote Site Settings → New 添加。
3. Validate and Sync 后——将 Deployment Status 设为 Deployed
Validate and Sync 后,Salesforce 会创建状态为 “In Development” 的 External Object。更改此状态之前,列表视图和 SOQL 查询都会返回 0 条记录。不会显示任何错误——这是另一个静默失败。
Setup → Object Manager → [YourObject]__x → Edit
Deployment Status → Deployed → Save
Validate and Sync 完成后,务必告知用户执行此步骤。
4. 创建选项卡,以便在 App Launcher 中显示 External Object
没有选项卡时,External Object 在 UI 中不可见。在此处创建:
Setup → Tabs → Custom Object Tabs → New → select [YourObject]__x → Save
另有以下必要条件
- Salesforce Connect 许可证——没有该许可证时,包括 OData 在内的所有 Connect 适配器类型都会被隐藏。如果用户在下拉列表中看到 OData 和 Cross-Org,说明许可证已激活。
反模式
| 不要 | 原因 | 应该这样做 |
|---|---|---|
| 在不了解 API 结构的情况下生成代码 | 字段类型错误、ExternalId 映射错误、适配器损坏 | 首先询问 API 名称、基本 URL、身份验证类型和示例响应 |
| 将 ExternalDataSource 作为元数据部署 | 这不是可部署的元数据类型——sf CLI 会报错 | 部署 Apex 类后,在 Setup 中手动注册 External Data Source |
使用 upsertRow() 或 deleteRow()(单数) | 这些方法不存在——会导致编译错误 | 使用 upsertRows() 和 deleteRows()(复数) |
只在 Provider 上放置 sync() | 在某些 API 版本中,sync() 必须位于 Connection 上——编译器会提示 | 在 Connection 上实现 sync();如果报错,则从 Provider 中移除 |
| 在 Apex 中硬编码 API URL | 凭据会暴露在源代码中,调用会被阻止 | 始终使用 callout:NamedCredentialName |
从 search() 返回单个 DataSource.TableResult | search() 返回 List<DataSource.TableResult>——会导致编译错误 | 遍历 context.tableSelections,并为每个表返回一个结果 |
在 query() 中跳过 COUNT 处理 | 列表视图会发起 COUNT 查询;不检测就会返回错误结果 | 检查 columnsSelected[0].aggregation == DataSource.QueryAggregation.COUNT |
| 将 External Object 保留为“In Development” | 记录会返回 0 条且没有错误——用户无法看到 | Validate and Sync 后:Object Manager → [Object]__x → Edit → Deployed |
| 跳过 Remote Site Setting | 调用会静默执行并返回空行,不会抛出异常 | 部署 remoteSiteSettings/[API].remoteSite-meta.xml,或在 Setup 中手动添加 |
发布前
确认:已收集输入(API 名称、URL、身份验证、示例响应、写入支持)→ 代码在 Connection 上包含 sync() + query() + search(),在 Provider 上包含 getCapabilities() + getConnection(),每个表都包含 ExternalId 和 nameColumn,query() 中包含 COUNT 处理,测试使用模拟数据,所有端点都带有 callout: 前缀 → 已生成两个 .cls-meta.xml 文件 → 已提供部署命令 → 已告知部署后的步骤(设置 Named Credential、Remote Site Setting、注册 External Data Source、Validate and Sync、将 Deployment Status 设为 Deployed、创建选项卡)。
参考示例
请参阅 references/official-examples.md,了解 Salesforce Connect 官方适配器示例的注释说明,包括 GitHub Issues(完整的 DML、选项列表、跨表关系)、Google Drive(OAuth + 测试模拟)、Google Books(分页)、StackOverflow(多个表)和 Loopback(筛选条件转换)。为新用户生成内容前请先阅读。
项目设置 — 如果开发者还没有 SFDX 项目
部署前,他们需要项目结构。如果尚不存在,请先生成:
sf project generate --name my-adapter --output-dir .
cd my-adapter
这会创建 force-app/main/default/classes/ 和必需的 sfdx-project.json。所有生成的文件都放入此结构中。开发者不需要 VS Code、Agentforce Vibes 或任何 Salesforce IDE,只需安装 sf CLI,并通过 sf org login web --alias my-org 完成组织身份验证。
此技能适用于任何能够读取上下文文件的编码 Agent — Claude Code、Cursor、Windsurf、GitHub Copilot,或任何已加载 SKILL.md 的 Agent。无需 Salesforce MCP 服务、Agentforce Vibes 或 Salesforce 内部工具。
输出格式
按以下顺序生成文件,然后提供部署命令:
force-app/main/default/classes/[API]DataSourceConnection.clsforce-app/main/default/classes/[API]DataSourceProvider.clsforce-app/main/default/classes/[API]DataSourceConnection.cls-meta.xmlforce-app/main/default/classes/[API]DataSourceProvider.cls-meta.xmlforce-app/main/default/namedCredentials/[API].namedCredential-meta.xml— 仅适用于公共 API
每个 Apex 类顶部都添加一行注释:// Salesforce Connect custom adapter for [API name]
除非逻辑选择不明显,否则不要添加其他行内注释。