diff --git a/.github/workflows/preview-docs.yml b/.github/workflows/preview-docs.yml index 675808397..15b123a81 100644 --- a/.github/workflows/preview-docs.yml +++ b/.github/workflows/preview-docs.yml @@ -30,9 +30,13 @@ jobs: id: generate-docs env: FERN_TOKEN: ${{ secrets.FERN_TOKEN }} - HEAD_REF: ${{ github.head_ref }} run: | - OUTPUT=$(fern generate --docs --preview --id "$HEAD_REF" --instance fern-api.docs.buildwithfern.com/learn 2>&1) || true + # Omit --id so each run gets a fresh preview hash. Pinning the id to the + # branch name makes the CLI reuse the same preview deploy across pushes, + # which masks staleness when reviewers re-test after a force-push or a + # CDN cache pin to old content. A throwaway preview per push is cheap + # and lines up 1:1 with the comment posted by this workflow. + OUTPUT=$(fern generate --docs --preview --instance fern-api.docs.buildwithfern.com/learn 2>&1) || true echo "$OUTPUT" URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K.*(?= \()') echo "preview_url=$URL" >> $GITHUB_OUTPUT @@ -80,5 +84,4 @@ jobs: uses: thollander/actions-comment-pull-request@v2.4.3 with: filePath: comment.md - comment_tag: preview-docs - mode: upsert + mode: create diff --git a/fern/docs.yml b/fern/docs.yml index 077b5db90..66300e89b 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -10,6 +10,11 @@ instances: title: Fern Documentation +translations: + - lang: en + default: true + - lang: zh + ai-search: location: - docs diff --git a/fern/fern.config.json b/fern/fern.config.json index c9d7305b4..788fb6032 100644 --- a/fern/fern.config.json +++ b/fern/fern.config.json @@ -1,4 +1,4 @@ { "organization": "fern", - "version": "4.62.3" + "version": "5.7.5" } diff --git a/fern/products/cli-api-reference/cli-changelog/2026-05-01.mdx b/fern/products/cli-api-reference/cli-changelog/2026-05-01.mdx index 027c5e208..f29765b95 100644 --- a/fern/products/cli-api-reference/cli-changelog/2026-05-01.mdx +++ b/fern/products/cli-api-reference/cli-changelog/2026-05-01.mdx @@ -37,7 +37,7 @@ spec. ## 5.6.1 -**`(fix):`** Fix translated docs pages failing to resolve shared snippets (`` and ``). +**`(fix):`** Fix translated docs pages failing to resolve shared snippets (Markdown and Code `src` includes). Snippet references are now resolved before registering translations, with locale-aware loading that prefers translated snippets (e.g., `translations/zh/snippets/foo.mdx`) when available. diff --git a/fern/translations/zh/fern/docs.yml b/fern/translations/zh/fern/docs.yml new file mode 100644 index 000000000..89056eef2 --- /dev/null +++ b/fern/translations/zh/fern/docs.yml @@ -0,0 +1,55 @@ +products: + - display-name: 首页 + path: ./products/home/home.yml + subtitle: 提升开发者体验的产品 + + - slug: sdks + display-name: SDK + path: ./products/sdks/sdks.yml + subtitle: 生成多种语言的客户端库 + + - slug: docs + display-name: 文档 + path: ./products/docs/docs.yml + subtitle: 生成精美的交互式文档网站 + + - slug: dashboard + display-name: 仪表板 + path: ./products/dashboard/dashboard.yml + subtitle: 管理您的 Fern 项目和设置 + + - slug: cli-api-reference + display-name: CLI 参考 + path: ./products/cli-api-reference/cli-api-reference.yml + subtitle: 管理和配置您的 Fern 项目 + + - slug: api-definitions + display-name: API 定义 + path: ./products/api-def/api-def.yml + subtitle: 导入和配置您的 API 规范 + + - display-name: OpenAPI + subtitle: 定义 REST 和 webhook API + + - display-name: AsyncAPI + subtitle: 定义事件驱动和 WebSocket API + + - display-name: OpenRPC + subtitle: 定义 JSON-RPC API + + - display-name: gRPC + subtitle: 使用 Protocol Buffers 定义 API + + - display-name: Fern 定义 + subtitle: 使用 Fern 的 YAML 格式定义 API + +navbar-links: + - type: minimal + text: 预约演示 + url: https://buildwithfern.com/book-demo?utm_source=fern-docs + - type: secondary + text: 登录 + url: https://dashboard.buildwithfern.com/login?utm_source=fern-docs + - type: filled + text: 免费开始 + url: https://dashboard.buildwithfern.com/sign-up?redirect_on_login=%2Fget-started&utm_source=fern-docs diff --git a/fern/translations/zh/fern/products/api-def/api-def.yml b/fern/translations/zh/fern/products/api-def/api-def.yml new file mode 100644 index 000000000..b08ac7372 --- /dev/null +++ b/fern/translations/zh/fern/products/api-def/api-def.yml @@ -0,0 +1,169 @@ +navigation: + - section: 概览 + contents: + - page: 什么是 API 定义? + - page: 项目结构 + - section: OpenAPI + slug: openapi + contents: + - page: 概览 + - page: 覆盖层(Overlays) + - page: 覆盖(Overrides) + - page: 身份验证 + - page: 服务器 + - page: 同步您的规范 + slug: sync-your-open-api-specification + - section: 端点 + slug: endpoints + contents: + - page: HTTP JSON 端点 + slug: http + - page: Webhooks + - page: 多部分表单上传 + slug: multipart + - page: 服务器发送事件 + slug: sse + - section: 扩展 + slug: extensions + contents: + - page: 概览 + - page: API 版本 + - page: 受众 + - page: 可用性 + - page: 基础路径 + - page: 默认值 + - page: 枚举描述、名称和可用性 + slug: enum-descriptions-and-names + - page: 请求与响应示例 + - page: API Explorer 控制 + - page: 全局请求头 + - page: 忽略元素 + - page: SDK 方法名称 + slug: method-names + - page: SDK 变量 + - page: 标签显示名称 + - page: 参数名称 + - page: 属性名称 + - page: 幂等性 + - page: 分页 + - page: 重试行为 + - page: Schema 名称 + - page: 服务器名称和 URL 模板化 + - section: 集成您的服务器框架 + slug: frameworks + contents: + - page: FastAPI + slug: fastapi + - page: generators.yml 参考 + - section: AsyncAPI + slug: asyncapi + contents: + - page: 概览 + - page: 覆盖(Overrides) + - page: 身份验证 + - page: 服务器 + - page: 同步您的规范 + slug: sync-your-async-api-specification + - section: 通道 + slug: channels + contents: + - page: 发布/订阅操作 + slug: pubsub + - page: 消息格式 + slug: messages + - page: 消息绑定 + slug: bindings + - section: 扩展 + slug: extensions + contents: + - page: 概览 + - page: 受众 + - page: 可用性 + - page: 请求与响应示例 + - page: 忽略元素 + - page: 服务器名称 + - section: OpenRPC + slug: openrpc + contents: + - page: 概览 + - page: 覆盖(Overrides) + - page: 身份验证 + - page: 服务器 + - page: 同步您的规范 + slug: sync-your-open-rpc-specification + - section: 方法 + slug: methods + contents: + - page: JSON-RPC 方法 + slug: rpc-methods + - section: 扩展 + slug: extensions + contents: + - page: 概览 + - page: 受众 + - page: 可用性 + - page: 请求与响应示例 + - page: 忽略元素 + - page: SDK 方法名称 + slug: method-names + - page: SDK 组名称 + - page: 服务器名称 + - section: gRPC + slug: grpc + contents: + - page: 概览 + - page: 覆盖(Overrides) + - page: 身份验证 + - page: 服务器 + - page: 同步您的规范 + slug: sync-your-g-rpc-specification + - section: 服务 + slug: services + contents: + - page: gRPC 服务 + slug: grpc-services + - page: 流式传输 + slug: streaming + - page: 错误处理 + slug: errors + - page: generators.yml 参考 + - section: Fern 定义 + slug: ferndef + contents: + - page: 概览 + - page: 身份验证 + - page: 类型 + - section: 端点 + contents: + - page: 概览 + - page: HTTP JSON 端点 + slug: http + - page: 多部分表单上传 + slug: multipart + - page: 字节 + slug: bytes + - page: 服务器发送事件 + slug: sse + - section: 高级 + contents: + - page: Webhooks + - page: WebSockets + slug: websockets + - page: 错误 + - page: 导入 + - page: 示例 + - page: 受众 + - page: 可用性 + - section: api.yml 参考 + slug: api-yml + contents: + - page: 概览 + - page: 环境 + - page: 全局请求头 + - page: 错误 + - section: 集成 + contents: + - page: 包 + - page: 依赖其他 API + - page: 导出到 OpenAPI + slug: export-openapi diff --git a/fern/translations/zh/fern/products/cli-api-reference/cli-api-reference.yml b/fern/translations/zh/fern/products/cli-api-reference/cli-api-reference.yml new file mode 100644 index 000000000..7b8e49765 --- /dev/null +++ b/fern/translations/zh/fern/products/cli-api-reference/cli-api-reference.yml @@ -0,0 +1,8 @@ +navigation: + - section: CLI 参考 + contents: + - page: 开始使用 Fern CLI + slug: overview + - page: 全局选项 + slug: options + - page: 命令 diff --git a/fern/translations/zh/fern/products/dashboard/dashboard.yml b/fern/translations/zh/fern/products/dashboard/dashboard.yml new file mode 100644 index 000000000..2fe07d4ce --- /dev/null +++ b/fern/translations/zh/fern/products/dashboard/dashboard.yml @@ -0,0 +1,15 @@ +navigation: + - section: 入门 + contents: + - page: 概览 + - section: 配置 + contents: + - page: 成员权限 + slug: permissions + - page: 连接 GitHub 仓库 + slug: github-repo + - page: 设置 SSO + slug: sso + - page: 密码保护 + - page: 自定义域名 + - page: PDF 导出 diff --git a/fern/translations/zh/fern/products/docs/docs.yml b/fern/translations/zh/fern/products/docs/docs.yml new file mode 100644 index 000000000..385746cf9 --- /dev/null +++ b/fern/translations/zh/fern/products/docs/docs.yml @@ -0,0 +1,248 @@ +navigation: + - section: 入门 + contents: + - page: 概览 + - page: 工作原理 + - page: 快速开始 + - page: 自助设置 + - page: 项目结构 + - section: 配置 + contents: + - page: 概览 + - page: 站点级设置 + slug: site-level-settings + - section: 导航 + contents: + - page: 章节、页面与文件夹 + slug: navigation + - page: 标签页与标签变体 + slug: tabs + - page: 版本 + - page: 产品 + - page: 更新日志页面 + slug: changelogs + - page: 页面级设置 + slug: page-level-settings + - section: 编写内容 + contents: + - page: Markdown 基础 + slug: markdown-basics + - page: Markdown 中的富媒体 + slug: markdown-media + - section: 组件 + contents: + - page: 概览 + - page: 折叠面板 + slug: accordions + - page: 锚点 + - page: 旁注 + - page: 徽章 + slug: badges + - page: 按钮 + - page: 提示框 + slug: callouts + - page: 卡片 + slug: cards + - page: 代码块 + slug: code-blocks + - page: 复制 + - page: 下载 + - page: 端点请求片段 + slug: request-snippet + - page: 端点响应片段 + slug: response-snippet + - page: 端点 Schema 片段 + - page: Webhook 负载片段 + slug: webhook-payload-snippet + - page: 文件 + slug: files + - page: 框架 + slug: frames + - page: 图标 + slug: icons + - page: If + slug: if + - page: 缩进 + slug: indent + - page: 参数字段 + slug: parameter-fields + - page: 可运行端点 + - page: Schema + - page: Schema 片段 + slug: schema-snippet + - page: 步骤 + slug: steps + - page: 表格 + slug: tables + - page: 标签页 + slug: tabs + - page: 工具提示 + slug: tooltips + - page: 版本 + slug: versions + - page: Fern 编辑器 + slug: fern-editor + - page: 可复用片段 + - page: 组件演示 + slug: demo + - section: AI 功能 + contents: + - page: 概览 + - page: Fern Writer + - page: AI 生成的示例 + slug: ai-examples + - section: llms-txt + contents: + - page: 概览 + slug: llms-txt + - page: 自定义 LLM 输出 + - page: Agent 指令 + - page: 分析与集成 + slug: llms-txt-analytics + - page: MCP 服务器 + slug: mcp-server + - section: Ask Fern + slug: ask-fern + contents: + - page: 概览 + slug: overview + - section: 设置 + contents: + - page: Slack 应用 + slug: slack-app + - page: 自定义指引 + slug: guidance + - page: 额外内容来源 + slug: content-sources + - page: 独立搜索小部件 + slug: search-widget + - section: 预览与发布 + contents: + - page: 预览更改 + - page: 发布您的文档 + - page: 设置您的域名 + - section: 自定义 + contents: + - page: 公告横幅 + - page: 嵌入模式 + - page: 隐藏内容 + - page: 隐藏页面示例 + slug: hidden-page + - page: 搜索 + - page: 用户反馈 + - page: 自定义 CSS 与 JS + - page: CSS 选择器参考 + - page: 自定义 React 组件 + - page: 页眉与页脚 + - page: 全局主题 + - section: 本地化 + contents: + - page: 概览 + - section: 无障碍 + contents: + - page: 概览 + - page: 键盘快捷键 + - section: API 参考 + contents: + - page: 概览 + - section: 生成 + contents: + - page: REST API 参考 + slug: generate-api-ref + - page: Webhook 参考 + slug: generate-webhook-ref + - page: WebSocket 参考 + slug: generate-websocket-ref + - page: OpenRPC 参考 + slug: generate-openrpc-ref + - page: gRPC 参考 + slug: generate-grpc-ref + - page: GraphQL 参考 + slug: generate-graphql-ref + - page: 库参考 + slug: library-reference + - section: 自定义 + contents: + - page: 自定义 API 参考布局 + - page: 受众 + - page: 在 API 参考中编写 Markdown + - section: 代码探索 + contents: + - page: SDK 代码片段 + - page: HTTP 代码片段 + - page: API Explorer + slug: api-explorer + - section: SEO 与 GEO + slug: seo + contents: + - page: 概览 + - page: 设置 SEO 元数据 + - page: 配置 slug + - page: 重定向 + - section: 身份验证 + contents: + - page: 概览 + - section: 设置 + slug: setup + contents: + - page: 密码保护 + - page: SSO + - page: JWT + slug: jwt + - page: OAuth + slug: oauth + - section: 功能 + slug: features + contents: + - page: 基于角色的访问控制 (RBAC) + slug: rbac + - page: API 密钥注入 + - section: 安全 + contents: + - page: 概览 + - section: 自托管 + contents: + - page: 概览 + - page: 设置自托管文档 + slug: set-up + - page: 身份验证 + slug: authentication + - page: 预览 + slug: previews + - page: 健康检查端点 + - section: 分析与集成 + slug: integrations + contents: + - page: 概览 + - page: Intercom + slug: support/intercom + - page: Google + slug: analytics/google + - page: PostHog + slug: analytics/posthog + - page: Fullstory + slug: analytics/fullstory + - page: Segment + slug: analytics/segment + - page: Mixpanel + slug: analytics/mixpanel + - page: Postman + - page: Context7 + slug: context7 + - page: LaunchDarkly 功能标志 + - section: 开发者工具 + contents: + - page: 编排 GitHub 发布 + - page: 自动更新最后更新日期 + - page: Cursor + - page: GitLab + - page: Vale + - page: 查看 Markdown + - page: 下载 OpenAPI 规范 + slug: openapi-spec + - page: 下载 AsyncAPI 规范 + slug: asyncapi-spec + - section: 资源 + contents: + - page: Stainless 与 Fern 对比 + slug: stainless-comparison diff --git a/fern/translations/zh/fern/products/home/home.yml b/fern/translations/zh/fern/products/home/home.yml new file mode 100644 index 000000000..3b818db6a --- /dev/null +++ b/fern/translations/zh/fern/products/home/home.yml @@ -0,0 +1,2 @@ +navigation: + - page: 首页 diff --git a/fern/translations/zh/fern/products/sdks/sdks.yml b/fern/translations/zh/fern/products/sdks/sdks.yml new file mode 100644 index 000000000..44435b1b9 --- /dev/null +++ b/fern/translations/zh/fern/products/sdks/sdks.yml @@ -0,0 +1,171 @@ +navigation: + - section: 概览 + contents: + - page: 简介 + - page: 工作原理 + - page: 快速开始 + slug: quickstart + - section: 使用 SDK + slug: overview + contents: + - page: 项目结构 + slug: project-structure + - page: 添加自定义代码 + slug: custom-code + - page: 功能特性 + slug: capabilities + - page: Fern Autorelease + slug: autorelease + - section: 生成器 + contents: + - section: TypeScript + slug: typescript + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 npm + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - page: 动态身份验证 + slug: dynamic-authentication + - page: 启用 serde 层 + slug: serde-layer + - section: Python + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 PyPI + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - page: 动态身份验证 + slug: dynamic-authentication + - page: aiohttp 支持 + slug: aiohttp-support + - section: Go + contents: + - page: 生成 SDK + slug: quickstart + - page: 作为 Go 模块发布 + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - section: Java + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 Maven Central + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - section: .NET + slug: csharp + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 NuGet + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - page: 版本兼容性 + - section: PHP + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 Packagist + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - section: Ruby + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 RubyGems + slug: publishing + - page: 配置 + slug: configuration + - page: 添加自定义代码 + slug: custom-code + - section: Swift + contents: + - page: 生成 SDK + slug: quickstart + - page: 作为 Swift 包发布 + slug: publishing + - page: 配置 + slug: configuration + - section: Rust + contents: + - page: 生成 SDK + slug: quickstart + - page: 发布到 crates.io + slug: publishing + - page: 配置 + slug: configuration + - section: Postman + contents: + - page: 快速开始 + slug: quickstart + - page: 发布到 Postman + slug: publishing + - section: SDK 设计 + slug: deep-dives + contents: + - page: SDK 用户特性 + slug: sdk-user-features + - page: 自定义 README + slug: readme + - page: 自定义方法名称 + slug: customize-method-names + - page: 按受众过滤端点 + slug: audiences + - page: 服务器 URL 模板化 + slug: server-url-templating + - page: Webhook 签名验证 + slug: webhook-signature-verification + - section: 请求行为 + slug: deep-dives + contents: + - page: 全局请求头 + slug: global-headers + - page: 动态身份验证 + slug: dynamic-authentication + - page: 自动分页 + slug: auto-pagination + - page: 幂等性请求头 + slug: idempotency + - page: 带退避的重试 + slug: retries-with-backoff + - section: 预览与测试 + slug: deep-dives + contents: + - page: 本地 SDK 预览 + slug: local-previews + - page: 测试 + slug: testing + - section: 托管 + slug: deep-dives + contents: + - page: 自托管 SDK + slug: self-hosted + - section: 参考 + contents: + - page: generators.yml + slug: generators-yml + - section: 资源 + contents: + - page: Fern 与 OpenAPI Generator 对比 + slug: openapi-generator diff --git a/fern/translations/zh/products/api-def/asyncapi/auth.mdx b/fern/translations/zh/products/api-def/asyncapi/auth.mdx new file mode 100644 index 000000000..fce6b483e --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/auth.mdx @@ -0,0 +1,185 @@ +--- +title: 身份验证 +subtitle: 为事件驱动的 API 建模身份验证方案,如 Bearer、Basic 和 API Key。 +sidebar-title: 身份验证 +--- + + +身份验证方案的配置在 AsyncAPI 的 `components.securitySchemes` 部分中进行。 + +```yml title="asyncapi.yml" {2-3} +components: + securitySchemes: + ... +``` + + +要在所有操作中应用安全方案,请在 AsyncAPI 规范的 `security` 部分引用 `securityScheme`。 + +```yml title="asyncapi.yml" {3, 5-6} +components: + securitySchemes: + AuthScheme: + ... +security: + - AuthScheme: [] +``` + + +## Bearer 安全方案 + +首先在 `asyncapi.yml` 中定义一个 `bearer` 安全方案: + +```yml title="asyncapi.yml" {3-5} +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer +``` + +这将生成一个 SDK,用户需要提供一个名为 `token` 的必需参数。 + +```ts index.ts +const client = new Client({ + token: "ey34..." +}) +``` + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yml title="asyncapi.yml" {4-6} +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + x-fern-token: + name: authToken + env: AUTH_TOKEN +``` + +## API Key 安全方案 + +首先在 `asyncapi.yml` 中定义一个 `apiKey` 安全方案: + +```yml title="asyncapi.yml" {3-6} +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key +``` + +这将生成一个 SDK,用户需要提供一个名为 `apiKey` 的必需参数。 + +```ts index.ts +const client = new Client({ + apiKey: "my-api-key" +}) +``` + +### 自定义 API Key 变量名称 + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yml title="asyncapi.yml" {5-7} +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + x-fern-api-key: + name: customApiKey + env: CUSTOM_API_KEY + in: header + name: X-API-Key +``` + +## Basic 安全方案 + +首先在 `asyncapi.yml` 中定义一个 `basic` 安全方案: + +```yml title="asyncapi.yml" {3-5} +components: + securitySchemes: + BasicAuth: + type: http + scheme: basic +``` + +这将生成一个 SDK,用户需要提供 `username` 和 `password`。 + +```ts index.ts +const client = new Client({ + username: "john@example.com", + password: "password123" +}) +``` + +### 自定义基本认证变量名称 + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yml title="asyncapi.yml" {4-7} +components: + securitySchemes: + BasicAuth: + type: http + scheme: basic + x-fern-username: + name: email + env: EMAIL + x-fern-password: + name: pass + env: PASSWORD +``` + +## OAuth2 安全方案 + + + +AsyncAPI 规范支持 OAuth2 身份验证: + +```yml title="asyncapi.yml" {3-11} +components: + securitySchemes: + OAuth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: https://example.com/oauth/authorize + tokenUrl: https://example.com/oauth/token + scopes: + read: Read access to resources + write: Write access to resources +``` + +这将生成一个支持事件驱动操作的 OAuth2 流程的 SDK。 + +## 多个安全方案 + +你可以指定多个安全方案并将它们应用于不同的操作: + +```yml title="asyncapi.yml" {2-12, 16-17} +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-Key + BearerAuth: + type: http + scheme: bearer + OAuth2: + type: oauth2 + flows: ... + +operations: + sendMessage: + security: + - ApiKeyAuth: [] + - BearerAuth: [] +``` + +这允许不同的操作根据需要使用不同的身份验证方法。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/automation.mdx b/fern/translations/zh/products/api-def/asyncapi/automation.mdx new file mode 100644 index 000000000..3afc1cd16 --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/automation.mdx @@ -0,0 +1,204 @@ +--- +title: 同步你的 AsyncAPI 规范 +subtitle: 自动同步你的 AsyncAPI 规范变更,保持 SDK 和文档的最新状态 +sidebar-title: 同步你的 AsyncAPI 规范 +--- + +保持你的 AsyncAPI 规范与代码库同步对于维护准确的 SDK 和文档至关重要。Fern 提供了几种自动化选项来简化这个过程。 + +## GitHub Actions + +使用 Fern 的 GitHub Action 在你的 AsyncAPI 规范发生变更时自动更新 SDK 和文档: + +```yaml title=".github/workflows/fern.yml" +name: Fern + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + fern-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Check AsyncAPI spec + uses: fern-api/action@v0 + with: + command: check + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + + fern-generate: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Generate SDKs and docs + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## Webhook 集成 + +设置 webhook 以在 AsyncAPI 规范更新时触发 SDK 生成: + +```yaml title="generators.yml" {4-8} +api: + specs: + - spec: asyncapi.yml + github: + repository: your-org/your-repo + webhooks: + - url: https://your-api.com/webhooks/fern + events: [generate] + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@your-org/sdk" +``` + +## 从源自动同步 + +配置 Fern 自动从各种源拉取你的 AsyncAPI 规范: + +### 从 URL +```yaml title="generators.yml" {3-4} +api: + specs: + - spec: https://api.yourcompany.com/asyncapi.yml + auto-sync: true + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +### 从 git 仓库 +```yaml title="generators.yml" {3-7} +api: + specs: + - spec: + git: + repository: https://github.com/your-org/api-specs + path: asyncapi/api.yml + branch: main + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +## CI/CD 集成 + +### CircleCI +```yaml title=".circleci/config.yml" {15-23} +version: 2.1 + +orbs: + fern: fernapi/fern@1.0 + +workflows: + version: 2 + build-and-test: + jobs: + - build + - test: + requires: + - build + - fern/generate: + requires: + - test + filters: + branches: + only: main + context: + - fern-context +``` + +### GitLab CI +```yaml title=".gitlab-ci.yml" {13-20} +stages: + - build + - test + - generate + +variables: + FERN_TOKEN: $FERN_TOKEN + +build: + stage: build + script: + - echo "Building application..." + +generate-sdks: + stage: generate + image: fernapi/fern:latest + script: + - fern generate + only: + - main +``` + +## 定时更新 + +设置定时更新以确保你的 SDK 保持最新: + +```yaml title=".github/workflows/scheduled-update.yml" +name: Scheduled AsyncAPI Update + +on: + schedule: + - cron: '0 2 * * 1' # Every Monday at 2 AM UTC + workflow_dispatch: + +jobs: + update-specs: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Update AsyncAPI specs + run: | + curl -o fern/asyncapi/asyncapi.yml https://api.yourcompany.com/asyncapi.yml + + - name: Generate with latest spec + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## 监控变更 + +跟踪你的 AsyncAPI 规范的变更: + +```yaml title="generators.yml" {4-9} +api: + specs: + - spec: asyncapi.yml + change-detection: + enabled: true + breaking-changes: error + notifications: + slack: ${{ secrets.SLACK_WEBHOOK }} + email: team@yourcompany.com + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +这确保了对你的 AsyncAPI 规范的任何破坏性变更都会被检测到,并且在变更传播到你的 SDK 和文档之前通知相应的团队成员。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/channels/bindings.mdx b/fern/translations/zh/products/api-def/asyncapi/channels/bindings.mdx new file mode 100644 index 000000000..d8493b72e --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/channels/bindings.mdx @@ -0,0 +1,215 @@ +--- +title: Message Bindings +subtitle: 为 WebSocket、MQTT、Kafka 和其他协议配置协议特定设置 +sidebar-title: Message Bindings +--- + +AsyncAPI 中的绑定允许您为服务器、通道、操作和消息指定协议特定信息。这使您能够为不同的消息协议提供详细配置。 + +```yml title="asyncapi.yml" {6-12} +channels: + user/notifications: + address: user/notifications + messages: + UserNotification: + $ref: '#/components/messages/UserNotification' + bindings: + ws: + method: GET + query: + type: object + properties: + token: + type: string + description: Authentication token +``` + +## WebSocket 绑定 + +为实时通信配置 WebSocket 特定设置: + +```yml title="asyncapi.yml" {4-10} +channels: + chat/room: + address: chat/room/{roomId} + bindings: + ws: + method: GET + headers: + type: object + properties: + Authorization: + type: string + query: + type: object + properties: + userId: + type: string + roomId: + type: string + +operations: + sendMessage: + action: send + channel: + $ref: '#/channels/chat~1room' + bindings: + ws: + type: request + method: message +``` + +## MQTT 绑定 + +为 IoT 和消息应用程序配置 MQTT 特定设置: + +```yml title="asyncapi.yml" {4-8} +channels: + device/telemetry: + address: device/{deviceId}/telemetry + bindings: + mqtt: + qos: 1 + retain: true + messageExpiryInterval: 3600 + +servers: + mqtt-broker: + host: mqtt.example.com + protocol: mqtt + bindings: + mqtt: + clientId: device-client + cleanSession: false + keepAlive: 60 + will: + topic: device/status + payload: "offline" + qos: 1 + retain: true +``` + +## Kafka 绑定 + +为高吞吐量事件流配置 Kafka 特定设置: + +```yml title="asyncapi.yml" {4-12} +channels: + user.events: + address: user.events + bindings: + kafka: + topic: user.events + partitions: 10 + replicas: 3 + topicConfiguration: + cleanup.policy: ["delete"] + retention.ms: 604800000 + segment.ms: 86400000 + +operations: + publishUserEvent: + action: send + channel: + $ref: '#/channels/user.events' + bindings: + kafka: + groupId: user-event-processors + key: + type: string + description: User ID for partitioning +``` + +## 服务器绑定 + +配置协议特定的服务器设置: + +```yml title="asyncapi.yml" {6-16} +servers: + websocket-server: + host: ws.example.com + protocol: wss + description: WebSocket server with SSL + bindings: + ws: + headers: + type: object + properties: + X-API-Version: + type: string + const: "v1" + + kafka-cluster: + host: kafka.example.com:9092 + protocol: kafka + bindings: + kafka: + schemaRegistryUrl: https://schema-registry.example.com + schemaRegistryVendor: confluent +``` + +## 消息绑定 + +配置特定协议如何处理消息: + +```yml title="asyncapi.yml" {8-15} +components: + messages: + OrderEvent: + name: OrderEvent + contentType: application/json + payload: + $ref: '#/components/schemas/Order' + bindings: + kafka: + key: + type: string + description: Order ID for consistent partitioning + headers: + type: object + properties: + eventType: + type: string + enum: [created, updated, cancelled] + mqtt: + qos: 2 + retain: false +``` + +## HTTP 绑定 + +用于基于 HTTP 的协议,如 Server-Sent Events: + +```yml title="asyncapi.yml" {4-8} +channels: + events/stream: + address: /events/stream + bindings: + http: + type: response + method: GET + query: + type: object + properties: + lastEventId: + type: string + description: Last received event ID for resumption + +operations: + streamEvents: + action: send + channel: + $ref: '#/channels/events~1stream' + bindings: + http: + type: response + statusCode: 200 + headers: + type: object + properties: + Content-Type: + type: string + const: text/event-stream +``` + +绑定提供了灵活性,可以利用协议特定功能,同时在不同传输机制之间维护统一的 API 规范。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/channels/messages.mdx b/fern/translations/zh/products/api-def/asyncapi/channels/messages.mdx new file mode 100644 index 000000000..c6d6362a0 --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/channels/messages.mdx @@ -0,0 +1,171 @@ +--- +title: 消息格式 +subtitle: 为事件驱动通信定义消息模式、内容类型和结构 +sidebar-title: 消息格式 +--- + +AsyncAPI 中的消息在 `components.messages` 部分定义,并从频道和操作中引用。它们定义了通过事件驱动 API 交换的数据的结构和格式。 + +```yml title="asyncapi.yml" {2-12} +components: + messages: + UserEvent: + name: UserEvent + title: User Event Message + summary: Event triggered by user actions + contentType: application/json + payload: + $ref: '#/components/schemas/UserEventPayload' + headers: + $ref: '#/components/schemas/MessageHeaders' +``` + +## 消息负载 + +负载定义了消息数据的结构: + +```yml title="asyncapi.yml" {4-6} +components: + messages: + OrderCreated: + contentType: application/json + payload: + $ref: '#/components/schemas/Order' + schemas: + Order: + type: object + properties: + id: + type: string + format: uuid + description: Unique order identifier + customerId: + type: string + description: Customer who placed the order + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + total: + type: number + format: decimal + description: Total order amount + createdAt: + type: string + format: date-time + description: When the order was created + required: + - id + - customerId + - items + - total +``` + +## 消息头 + +您可以定义与消息一起发送的头: + +```yml title="asyncapi.yml" {6-8} +components: + messages: + NotificationEvent: + contentType: application/json + payload: + $ref: '#/components/schemas/Notification' + headers: + $ref: '#/components/schemas/NotificationHeaders' + schemas: + NotificationHeaders: + type: object + properties: + messageId: + type: string + description: Unique message identifier + timestamp: + type: string + format: date-time + description: Message timestamp + source: + type: string + description: Source service that generated the message + priority: + type: string + enum: [low, medium, high, urgent] + description: Message priority level +``` + +## 内容类型 + +AsyncAPI 支持各种消息内容类型: + +```yml title="asyncapi.yml" {4,10,16} +components: + messages: + JsonMessage: + contentType: application/json + payload: + $ref: '#/components/schemas/JsonPayload' + + BinaryMessage: + contentType: application/octet-stream + payload: + type: string + format: binary + + TextMessage: + contentType: text/plain + payload: + type: string +``` + +## 消息示例 + +提供具体示例以帮助开发者理解您的消息格式: + +```yml title="asyncapi.yml" {8-18} +components: + messages: + UserSignup: + name: UserSignup + title: User Signup Event + contentType: application/json + payload: + $ref: '#/components/schemas/User' + examples: + - name: StandardSignup + summary: Regular user signup + payload: + id: "123e4567-e89b-12d3-a456-426614174000" + email: "john@example.com" + name: "John Doe" + signupSource: "web" + createdAt: "2024-01-15T10:30:00Z" +``` + +## 消息特性 + +使用特性在多个消息之间共享通用消息属性: + +```yml title="asyncapi.yml" {2-12, 15-19} +components: + messageTraits: + commonHeaders: + headers: + type: object + properties: + messageId: + type: string + timestamp: + type: string + format: date-time + + messages: + UserEvent: + traits: + - $ref: '#/components/messageTraits/commonHeaders' + contentType: application/json + payload: + $ref: '#/components/schemas/UserEventPayload' +``` + +这种方法有助于在消息定义中保持一致性,同时减少重复。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/channels/pubsub.mdx b/fern/translations/zh/products/api-def/asyncapi/channels/pubsub.mdx new file mode 100644 index 000000000..690373f3f --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/channels/pubsub.mdx @@ -0,0 +1,106 @@ +--- +title: 发布/订阅操作 +description: 为事件驱动的 API 定义 AsyncAPI 发布/订阅操作。包含消息示例和双向通道的分步教程。 +sidebar-title: 发布/订阅操作 +--- + +AsyncAPI 中的操作定义在 `operations` 键下,通道定义在 `channels` 键下。以下是定义发布和订阅操作的示例: + +```yml title="asyncapi.yml" maxLines=0 {2-22} +channels: + user/notifications: + address: user/notifications + messages: + UserNotification: + $ref: '#/components/messages/UserNotification' + +operations: + onUserNotification: + action: receive + channel: + $ref: '#/channels/user~1notifications' + summary: Receive user notifications + description: Subscribe to user notification events + sendUserNotification: + action: send + channel: + $ref: '#/channels/user~1notifications' + summary: Send user notification + description: Publish a user notification event +``` + +## 消息示例 + +您可以通过在消息定义中使用 `examples` 键来提供消息示例: + +```yaml title="asyncapi.yml" {8-15} +components: + messages: + UserNotification: + name: UserNotification + title: User Notification + summary: Notification sent to a user + contentType: application/json + payload: + $ref: '#/components/schemas/Notification' + examples: + - name: EmailNotification + summary: Example email notification + payload: + userId: "123e4567-e89b-12d3-a456-426614174000" + type: "email" + message: "Welcome to our service!" + priority: "medium" +``` + +## 发布操作 + +发布操作表示您的服务发送到通道的消息: + +```yml title="asyncapi.yml" {2-8} +operations: + publishOrderStatus: + action: send + channel: + $ref: '#/channels/order~1status' + summary: Publish order status update + description: Send order status updates to subscribers + message: + $ref: '#/components/messages/OrderStatus' +``` + +## 订阅操作 + +订阅操作表示您的服务从通道接收的消息: + +```yml title="asyncapi.yml" {2-8} +operations: + subscribeToUserSignups: + action: receive + channel: + $ref: '#/channels/user~1signup' + summary: Subscribe to user signups + description: Receive notifications when users sign up + message: + $ref: '#/components/messages/UserSignup' +``` + +## 双向通信 + +您可以为同一个通道定义发布和订阅操作,以启用双向通信: + +```yml title="asyncapi.yml" {2-15} +operations: + sendChatMessage: + action: send + channel: + $ref: '#/channels/chat~1room' + summary: Send chat message + description: Send a message to a chat room + receiveChatMessage: + action: receive + channel: + $ref: '#/channels/chat~1room' + summary: Receive chat message + description: Receive messages from a chat room +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/extensions/audiences.mdx b/fern/translations/zh/products/api-def/asyncapi/extensions/audiences.mdx new file mode 100644 index 000000000..4fd52138d --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/extensions/audiences.mdx @@ -0,0 +1,160 @@ +--- +title: 使用受众筛选您的 API +headline: 使用受众筛选您的 API (AsyncAPI) +subtitle: 使用 `x-fern-audiences` 筛选相关的操作、通道和消息模式 +sidebar-title: 使用受众筛选 API +--- + + + + +受众是对事件驱动 API 进行分段以服务不同消费者的有用工具。常见的受众示例包括 `public` 和 `beta`。 + + + 记住在指定受众后要筛选您的 SDK 和文档。如果**未指定受众**,将不会进行任何筛选。 + + + + +以下示例配置 SDK 筛选到 `public` 受众: + +```yaml title="generators.yml" {3-4} +groups: + sdks: + audiences: + - public + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + + + +以下示例配置文档筛选到 `public` 受众: + +```yaml title="docs.yml" {3-4} +navigation: + - api: API Reference + audiences: + - public +``` + + + + + +## 筛选操作 + +在操作中添加 `x-fern-audiences` 来控制特定受众包含哪些操作: + +```yaml title="asyncapi.yml" {5-6, 12-13} +operations: + sendPublicNotification: + action: send + channel: + $ref: '#/channels/public~1notifications' + x-fern-audiences: + - public + sendBetaAlert: + action: send + channel: + $ref: '#/channels/beta~1alerts' + x-fern-audiences: + - beta +``` + +## 筛选通道 + +您还可以按受众筛选整个通道: + +```yaml title="asyncapi.yml" {6-7, 13-14} +channels: + public/events: + address: public/events + messages: + PublicEvent: + $ref: '#/components/messages/PublicEvent' + x-fern-audiences: + - public + internal/events: + address: internal/events + messages: + InternalEvent: + $ref: '#/components/messages/InternalEvent' + x-fern-audiences: + - internal +``` + +## 筛选消息模式 + +将特定消息模式筛选到不同受众: + +```yaml title="asyncapi.yml" {5-6, 13-14} +components: + messages: + PublicUserEvent: + contentType: application/json + payload: + $ref: '#/components/schemas/PublicUser' + x-fern-audiences: + - public + AdminUserEvent: + contentType: application/json + payload: + $ref: '#/components/schemas/AdminUser' + x-fern-audiences: + - admin + schemas: + PublicUser: + type: object + properties: + id: + type: string + name: + type: string + email: + type: string + x-fern-audiences: + - public + AdminUser: + allOf: + - $ref: '#/components/schemas/PublicUser' + - type: object + properties: + role: + type: string + permissions: + type: array + items: + type: string + x-fern-audiences: + - admin +``` + +## 筛选模式属性 + +您可以筛选模式内的单个属性: + +```yaml title="asyncapi.yml" {9-10, 13-14} +components: + schemas: + UserEvent: + type: object + properties: + id: + type: string + email: + type: string + x-fern-audiences: + - internal + publicName: + type: string + x-fern-audiences: + - public + internalNotes: + type: string + x-fern-audiences: + - internal +``` + +这使您能够为不同受众提供相同事件模式的不同视图,向每个消费者只显示相关信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/extensions/availability.mdx b/fern/translations/zh/products/api-def/asyncapi/extensions/availability.mdx new file mode 100644 index 000000000..43dbf2f0e --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/extensions/availability.mdx @@ -0,0 +1,19 @@ +--- +title: 可用性 +headline: 可用性 (AsyncAPI) +description: 使用 `x-fern-availability` 扩展标记功能在特定 SDK 版本中的可用性 +sidebar-title: 可用性 +--- + +标记功能在特定 SDK 版本中的可用性: + +```yaml title="asyncapi.yml" {6-8} +operations: + newFeature: + action: send + channel: + $ref: '#/channels/new~1feature' + x-fern-availability: + status: beta + message: "This feature is in beta and may change" +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/extensions/examples.mdx b/fern/translations/zh/products/api-def/asyncapi/extensions/examples.mdx new file mode 100644 index 000000000..f9809b1bf --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/extensions/examples.mdx @@ -0,0 +1,34 @@ +--- +title: 请求和响应示例 +headline: 请求和响应示例 (AsyncAPI) +description: 使用 `x-fern-examples` 向 AsyncAPI 规范添加消息示例。通过真实世界的载荷示例改善 SDK 文档。 +og:title: AsyncAPI 请求和响应示例 +sidebar-title: 请求和响应示例 +--- + + +为更好的 SDK 文档提供额外示例: + +```yaml title="asyncapi.yml" {8-20} +components: + messages: + UserEvent: + contentType: application/json + payload: + $ref: '#/components/schemas/User' + x-fern-examples: + - name: NewUserSignup + summary: Example of a new user signup event + payload: + id: "user_123" + email: "john@example.com" + name: "John Doe" + status: "active" + - name: UserDeactivation + summary: Example of user deactivation event + payload: + id: "user_456" + email: "jane@example.com" + name: "Jane Smith" + status: "inactive" +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/extensions/ignore.mdx b/fern/translations/zh/products/api-def/asyncapi/extensions/ignore.mdx new file mode 100644 index 000000000..c115f3b08 --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/extensions/ignore.mdx @@ -0,0 +1,27 @@ +--- +title: 忽略操作、通道或模式 +headline: 忽略操作、通道或模式 (AsyncAPI) +description: 了解如何使用 x-fern-ignore 在 AsyncAPI SDK 生成中排除特定的操作、通道或模式。 +sidebar-title: 忽略操作、通道或模式 +--- + + +使用 `x-fern-ignore` 在 SDK 生成中排除特定的操作、通道或模式: + +```yaml title="asyncapi.yml" {6-7, 13-14} +operations: + debugOperation: + action: send + channel: + $ref: '#/channels/debug' + x-fern-ignore: true + summary: Debug operation (internal only) + +channels: + internal/debug: + address: internal/debug + x-fern-ignore: true + messages: + DebugMessage: + $ref: '#/components/messages/DebugMessage' +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/extensions/server-name.mdx b/fern/translations/zh/products/api-def/asyncapi/extensions/server-name.mdx new file mode 100644 index 000000000..e86ac4e9a --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/extensions/server-name.mdx @@ -0,0 +1,21 @@ +--- +title: 服务器名称 +headline: 服务器名称 (AsyncAPI) +description: 在 AsyncAPI 规范中使用 `x-fern-server-name` 扩展配置自定义服务器名称。设置生产和测试服务器标签。 +sidebar-title: 服务器名称 +--- + + +为服务器指定自定义名称: + +```yaml title="asyncapi.yml" {4-5} +servers: + production: + host: api.yourcompany.com + x-fern-server-name: Production + protocol: wss + staging: + host: staging.api.yourcompany.com + x-fern-server-name: Staging + protocol: wss +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/overview.mdx b/fern/translations/zh/products/api-def/asyncapi/overview.mdx new file mode 100644 index 000000000..0d5dc96fb --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/overview.mdx @@ -0,0 +1,183 @@ +--- +title: 什么是 AsyncAPI 规范? +description: AsyncAPI 是用于文档化事件驱动 API 的标准 +sidebar-title: 什么是 AsyncAPI 规范? +--- + + + + Fern 支持为 TypeScript、Python、Java 和 C# 生成 AsyncAPI SDK。[提交 issue](https://github.com/fern-api/fern/issues) 来请求支持其他语言。 + + +AsyncAPI 规范是开发人员用来文档化事件驱动 API 的框架。该规范使用 JSON 或 YAML 编写,包含您的所有通道、消息、模式和身份验证方案。Fern 与 AsyncAPI 规范 [v2.6.0](https://www.asyncapi.com/docs/reference/specification/v2.6.0) 和 [v3.0.0](https://www.asyncapi.com/docs/reference/specification/v3.0.0) 兼容。 + +以下是一个 AsyncAPI 文件的示例: + +```yaml asyncapi.yml +asyncapi: 3.0.0 +info: + title: User Notification Service + version: 1.0.0 + description: | + Service that handles user notifications through various channels + including email, SMS, and push notifications. +channels: + user/signup: + address: user/signup + messages: + UserSignedUp: + $ref: '#/components/messages/UserSignedUp' + notification/send: + address: notification/send + messages: + SendNotification: + $ref: '#/components/messages/SendNotification' +operations: + onUserSignup: + action: receive + channel: + $ref: '#/channels/user~1signup' + summary: User signup event + description: Triggered when a user signs up for the service + sendNotification: + action: send + channel: + $ref: '#/channels/notification~1send' + summary: Send notification + description: Send a notification to a user +components: + messages: + UserSignedUp: + name: UserSignedUp + title: User Signed Up + summary: User has signed up for the service + contentType: application/json + payload: + $ref: '#/components/schemas/User' + SendNotification: + name: SendNotification + title: Send Notification + summary: Send a notification to a user + contentType: application/json + payload: + $ref: '#/components/schemas/Notification' + schemas: + User: + type: object + properties: + id: + type: string + format: uuid + description: Unique identifier for the user + email: + type: string + format: email + description: User's email address + name: + type: string + description: User's full name + createdAt: + type: string + format: date-time + description: When the user was created + required: + - id + - email + - name + Notification: + type: object + properties: + userId: + type: string + format: uuid + description: ID of the user to notify + type: + type: string + enum: [email, sms, push] + description: Type of notification to send + message: + type: string + description: Message content + priority: + type: string + enum: [low, medium, high, urgent] + default: medium + description: Priority level of the notification + required: + - userId + - type + - message +``` + +## 设置您的 fern 文件夹 + + + 正在考虑生成 AsyncAPI 规范的选项?在[这里](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)获得实时支持 + + + + +在您的项目根目录中创建一个 `fern/` 文件夹。 + + + + + + +将您的 AsyncAPI 规范添加到 fern 目录中。您可以将其放在名为 `asyncapi` 的子文件夹中,或直接放在 fern 目录中。 + + + + + + + + + + +在您的 fern 目录中添加一个 `fern.config.json` 文件,其中列出您的组织和当前版本的 Fern CLI: + +```json title="fern.config.json" +{ + "organization": "your-organization", + "version": "" +} +``` + + + + + + + + + + + +在您的 fern 目录中创建一个 `generators.yml` 文件,并添加对您的 AsyncAPI 规范的引用: + +```yaml title="generators.yml" +# Your API definition +api: + specs: + - asyncapi: ./asyncapi/asyncapi.yml + +groups: + external: + generators: + # Your generator configurations here +``` + +您的最终目录结构: + + + + + + + + + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/asyncapi/servers.mdx b/fern/translations/zh/products/api-def/asyncapi/servers.mdx new file mode 100644 index 000000000..70dead6bb --- /dev/null +++ b/fern/translations/zh/products/api-def/asyncapi/servers.mdx @@ -0,0 +1,117 @@ +--- +title: 服务器 +description: 配置服务器 URL 和协议以帮助用户连接到您的事件驱动 API。 +subtitle: 定义服务器 URL 和协议以帮助用户连接到您的事件驱动 API。 +sidebar-title: 服务器 +--- + +AsyncAPI 允许您在 `servers` 键下指定一个或多个服务器配置。 + +```yml asyncapi.yml + +servers: + production: + host: api.yourcompany.com + protocol: wss + description: Production WebSocket server + staging: + host: staging.api.yourcompany.com + protocol: wss + description: Staging WebSocket server +``` + +指定服务器对 SDK 和文档都很有价值: +- 对于 SDK,您的用户在客户端实例化时无需手动指定服务器 URL +- 对于文档,您的 API 游乐场将自动连接到正确的服务器 + +## 协议支持 + +AsyncAPI 支持各种用于事件驱动通信的协议: + +```yml asyncapi.yml {4,8,12} +servers: + websocket-server: + host: ws.api.yourcompany.com + protocol: ws + description: WebSocket server for real-time communication + mqtt-server: + host: mqtt.yourcompany.com + protocol: mqtt + description: MQTT broker for IoT devices + kafka-server: + host: kafka.yourcompany.com + protocol: kafka + description: Kafka cluster for event streaming +``` + +## 命名您的服务器 + +我们建议为您的服务器提供描述性名称,以明确每个服务器的用途: + +```yml asyncapi.yml +servers: + production: + host: api.yourcompany.com + protocol: wss + description: Production WebSocket server + staging: + host: staging.api.yourcompany.com + protocol: wss + description: Staging environment for testing + development: + host: localhost:8080 + protocol: ws + description: Local development server +``` + +## 服务器变量 + +您可以在服务器配置中使用变量来使其更加灵活: + +```yml asyncapi.yml {3-10} +servers: + production: + host: '{environment}.api.yourcompany.com' + protocol: wss + variables: + environment: + default: prod + enum: + - prod + - staging + description: Environment name +``` + +## 不同通道使用多种协议 + +如果您有使用不同协议的不同通道,您可以在服务器配置中指定这一点: + +```yml asyncapi.yml {2-14, 17-21} +servers: + websocket-server: + host: ws.api.yourcompany.com + protocol: wss + description: WebSocket server for real-time notifications + mqtt-server: + host: mqtt.api.yourcompany.com + protocol: mqtt + description: MQTT broker for IoT device communication + kafka-server: + host: kafka.api.yourcompany.com + protocol: kafka + description: Kafka for high-throughput event streaming + +channels: + user/notifications: + servers: + - websocket-server + address: user/notifications + description: Real-time user notifications via WebSocket + device/telemetry: + servers: + - mqtt-server + address: device/telemetry + description: IoT device telemetry via MQTT +``` + +这允许不同的通道使用最适合其用例的协议。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/api-yml/environments.mdx b/fern/translations/zh/products/api-def/ferndef/api-yml/environments.mdx new file mode 100644 index 000000000..9f6ece096 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/api-yml/environments.mdx @@ -0,0 +1,132 @@ +--- +title: 环境配置 +description: 列出生产、预发布和开发等环境。 +sidebar-title: 环境配置 +--- + +您可以指定服务器部署的环境。 + +## 单 URL 环境 + +```yaml title="api.yml" +name: api +environments: + Production: https://www.yoursite.com + Staging: + docs: This staging environment is helpful for testing! + url: https://www.staging.yoursite.com +``` + +## 每个环境多个 URL + +您可以为每个环境指定多个 URL。如果您有微服务架构,并且希望单个 SDK 与多个服务器交互,这将很有帮助。 + +```yaml title="api.yml" +environments: + Production: + urls: + Auth: https://auth.yoursite.com + Plants: https://plants.yoursite.com + Staging: + urls: + Auth: https://auth.staging.yoursite.com + Plants: https://plants.staging.yoursite.com +``` + +如果您选择使用此功能,必须为您定义的每个服务指定一个 `url`: + +```yaml title="auth.yml" +service: + url: Auth + base-path: /auth + ... +``` + +## 默认环境 + +您也可以提供默认环境: + +```yaml title="api.yml" +name: api +environments: + Production: https://www.yoursite.com + Staging: + docs: This staging environment is helpful for testing! + url: https://www.staging.yoursite.com +default-environment: Production +``` + +通过提供默认环境,生成的 SDK 将设置为开箱即用地访问该 URL。 + +## URL 模板 + +URL 模板目前仅支持 Python 和 Java SDK 生成。 + +对于跨多个区域或环境部署的 API,您可以定义带有变量占位符的 URL 模板,SDK 用户可以在运行时进行自定义。要设置此功能: + +1. 在 `urls` 下定义您的静态基础 URL——这些会出现在生成的环境枚举中。 +2. 为每个服务添加带有 `{variable}` 占位符的 `url-templates`(例如,`https://api.{region}.example.com/v1`)。Fern 将这些作为 SDK 中的可配置参数公开。 +3. 提供 `default-urls` 作为具体的回退选项,这样 SDK 用户无需提供变量就能获得开箱即用的客户端。 +4. 为每个服务列出可用的 `variables`,每个变量都有一个 `id`、一个 `default` 值,以及一个可选的 `values` 列表来约束允许的选项。 + +```yaml title="api.yml" +environments: + RegionalApiServer: + urls: + Base: https://api.example.com/v1 + Auth: https://auth.example.com + url-templates: + Base: https://api.{region}.{environment}.example.com/v1 + Auth: https://auth.{region}.example.com + default-urls: + Base: https://api.example.com/v1 + Auth: https://auth.example.com + variables: + Base: + - id: region + default: us-east-1 + values: + - us-east-1 + - us-west-2 + - eu-west-1 + - id: environment + default: prod + values: + - prod + - staging + - dev + Auth: + - id: region + default: us-east-1 + values: + - us-east-1 + - us-west-2 + - eu-west-1 +default-environment: RegionalApiServer +``` + +## 基础路径 +如果您希望所有端点都添加路径前缀,请使用 `base-path`。 + +在下面的示例中,每个端点都添加了 `/v1` 前缀: +```yaml title="api.yml" +name: api +base-path: /v1 +``` + +## 受众 + +如果您有列出的环境需要过滤,可以利用受众功能。 + +```yaml title="api.yml" +audiences: + - public + +environments: + Dev: + url: https://api.dev.buildwithfern.com + Prod: + url: https://api.buildwithfern.com + audiences: + - external +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/api-yml/errors.mdx b/fern/translations/zh/products/api-def/ferndef/api-yml/errors.mdx new file mode 100644 index 000000000..2546acd56 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/api-yml/errors.mdx @@ -0,0 +1,49 @@ +--- +title: 错误处理 +description: 指定错误类型和模式 +sidebar-title: 错误处理 +--- + +为了以惯用方式生成 SDK,Fern 需要知道在解析端点响应时如何区分不同的错误。 + +### 按状态码区分 + +您可以指定 Fern 按状态码区分。这意味着在每个端点上,列出的每个错误都必须具有不同的 HTTP 状态码。 + + +```yaml +name: api +error-discrimination: + strategy: status-code +``` + + +### 按错误名称区分 + +您可以指定 Fern 按错误名称区分。如果您选择此策略,那么 Fern 将假设每个错误响应都有一个额外的属性来表示错误名称。 + +如果您使用 Fern 生成服务器端代码,那么此选项提供了最大的灵活性。否则,您可能希望使用状态码区分策略。 + + +```yaml +name: api +error-discrimination: + strategy: property + property-name: errorName +``` + + +### 全局错误 + +您可以导入并列出将由每个端点抛出的错误。 + + +```yaml +imports: + commons: commons.yml + +errors: + - commons.NotFoundError + - commons.BadRequestError +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/api-yml/global-configuration.mdx b/fern/translations/zh/products/api-def/ferndef/api-yml/global-configuration.mdx new file mode 100644 index 000000000..8445f0a39 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/api-yml/global-configuration.mdx @@ -0,0 +1,69 @@ +--- +title: 全局配置 +description: 指定全局请求头、路径参数或查询参数,以包含在每个请求中。 +sidebar-title: 全局配置 +--- + +`api.yml` 配置支持全局配置,如请求头和路径参数。 + +## 全局请求头 + +您可以指定要包含在每个请求中的请求头: + + +```yaml +name: api +headers: + X-App-Id: string +``` + + +在 `api.yml` 中定义全局请求头时,您必须[在端点示例中包含它们](/api-definitions/ferndef/examples#examples-with-headers)。 + +## 全局路径参数 + +您可以指定要包含在每个请求中的路径参数: + + +```yaml +name: api +base-path: /{userId}/{orgId} +path-parameters: + userId: string + orgId: string +``` + + +### 覆盖基础路径 + +如果您有某些端点不在配置的 `base-path` 下,您可以在端点级别覆盖 `base-path`。 + +```yml imdb.yml {5} +service: + endpoints: + getMovie: + method: POST + base-path: "override/{arg}" + path: "movies/{movieId}" + path-parameters: + arg: string +``` + +## 全局查询参数 + +目前还不能指定要包含在每个请求中的查询参数。 +如果您希望看到这个功能,请为[此问题](https://github.com/fern-api/fern/issues/2930)投票。 + +## 幂等性请求头 + +配置幂等性请求头来定义 [SDK 用户](/learn/sdks/deep-dives/idempotency)可以为安全请求重试指定的请求头。您还必须[将每个端点标记为幂等](/learn/api-definitions/ferndef/endpoints/overview#idempotent-endpoints)才能公开这些请求头。当两者都配置时,Fern 生成的 SDK 会将这些请求头作为幂等端点调用的参数公开。 + +```yaml title="api.yml" +name: api +auth: bearer +idempotency-headers: + Idempotency-Key: string + Idempotency-Expiration: optional +``` + +`idempotency-headers` 中的每个键是 HTTP 请求头名称,值是类型。然后 [SDK 用户](/learn/sdks/deep-dives/idempotency)可以在调用幂等端点时指定这些请求头。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/api-yml/overview.mdx b/fern/translations/zh/products/api-def/ferndef/api-yml/overview.mdx new file mode 100644 index 000000000..fc5c27f04 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/api-yml/overview.mdx @@ -0,0 +1,57 @@ +--- +title: api.yml 配置文件 +description: 使用 Fern Definition 格式时,api.yml 文件包含通用 API 配置。 +sidebar-title: api.yml 配置文件 +--- + +`fern/` 文件夹中有一个名为 `api.yml` 的特殊文件,其中包含所有 API 级别的配置。 + +```bash {5} +fern/ +├─ fern.config.json +├─ generators.yml +└─ definition/ + ├─ api.yml + ├─ pet.yml + ├─ store.yml + └─ user.yml +``` + +## API 名称 + +此名称用于在您的组织中唯一标识您的 API。如果您只有一个 API,那么 `api` 是一个合适的名称。 + + +```yaml +name: api +``` + + +## API 描述 + +您可以定义顶级 API 描述。此描述将出现在 OpenAPI 规范和 Postman 集合中。 + + +```yaml {2-4} +name: api +docs: | + ## Header + This API provides access to... +``` + + +## API 版本 + +您可以定义基于请求头的 API 版本控制方案,例如 `X-API-Version`。支持的版本和默认值的指定方式如下: + + +```yaml +version: + header: X-API-Version + default: "2.0.0" + values: + - "1.0.0" + - "2.0.0" + - "latest" +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/audiences.mdx b/fern/translations/zh/products/api-def/ferndef/audiences.mdx new file mode 100644 index 000000000..c0f34a865 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/audiences.mdx @@ -0,0 +1,116 @@ +--- +title: Fern Definition 中的受众 +subtitle: 在您的 Fern Definition 中使用受众,为不同的 API 消费者群体进行分组。 +sidebar-title: Fern Definition 中的受众 +--- + + + + +受众是为不同消费者分组 API 的有用工具。您可以配置 Fern Docs 发布特定于某个`受众`的文档。您也可以在 [OpenAPI 规范中使用受众](/learn/api-definitions/openapi/extensions/audiences)。 + +受众的常见示例包括: + +- 内部消费者(例如,使用 API 的前端开发人员) +- Beta 测试人员 +- 客户 + +默认情况下,如果未指定受众,则所有消费者都可以访问。 + +## 配置 + +Fern Definition 具有为不同端点、类型和属性标记不同受众的一级概念。 + +要在 Fern Definition 中使用受众,请将其添加到 `api.yml` 中。 + +在下面的示例中,我们为 `internal`、`beta` 和 `customer` 群体创建了受众: + +```yaml title='api.yml' {2-5} +name: api +audiences: + - internal + - beta + - customers +``` + +## 端点的受众 + +要为特定消费者标记端点,请添加包含相关群体的 `audience`。 + +在此示例中,`sendEmail` 端点仅对内部消费者可用: + +```yaml title='user.yml' {6-7} +service: + base-path: /users + auth: true + endpoints: + sendEmail: + audiences: + - internal + path: /send-email + ... +``` + +## 类型的受众 + +类型也可以标记为不同的受众。 + +在此示例中,`Email` 类型对内部和 beta 消费者可用: + +```yaml title='user.yml' {5-7} +Email: + properties: + subject: string + body: optional + audiences: + - internal + - beta +``` + +## 属性的受众 + +类型的属性也可以标记为不同的受众。 + +在此示例中,`to` 属性仅对 beta 消费者可用: + +```yaml title='user.yml' {8-9} +Email: + properties: + subject: string + body: optional + to: + type: string + docs: The recipient of the email + audiences: + - beta +``` + +## SDK 的受众 + +在 `generators.yml` 中,您可以应用受众过滤器,以便只有某些端点传递给生成器。 + +以下示例配置 SDK 过滤 `customers`: + +```yaml title='generators.yml' {3-4} +groups: + external: + audiences: + - customers + generators: + ... +``` + +## 文档的受众 + +如果生成 Fern Docs,请更新您的 `docs.yml` 配置以包含您的受众。 + +以下示例展示了如何配置您的 `docs.yml` 为 `customers` 受众发布文档: + + +```yaml {3-4} +navigation: + - api: API Reference + audiences: + - customers +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/auth.mdx b/fern/translations/zh/products/api-def/ferndef/auth.mdx new file mode 100644 index 000000000..dfbf786e0 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/auth.mdx @@ -0,0 +1,258 @@ +--- +title: 身份验证 +description: 在 Fern Definition 中配置 API 身份验证。为您的 API 端点设置 bearer token、基本身份验证、自定义头部和 OAuth。 +sidebar-title: 身份验证 +--- + +身份验证方案的配置在 `api.yml` 文件中进行。所有 Fern 生成的 SDK 都支持直接配置和环境变量来配置身份验证凭据。 + +```bash {5} +fern/ +├─ fern.config.json # 根级别配置 +├─ generators.yml # 你正在使用的生成器 +└─ definition/ + ├─ api.yml # API 级别配置 + └─ imdb.yml # 端点、类型和错误 +``` + +要添加身份验证方案,请在 `auth-schemes` 部分下指定身份验证方法。 + +```yaml api.yml {1-2} +auth-schemes: + AuthScheme: + ... +``` + + +要在所有端点中应用身份验证方案,请在 `api.yml` 文件的 `auth` 部分中引用 `auth-scheme`。 +```yaml api.yml {1} +auth: AuthScheme +auth-schemes: + AuthScheme: + ... +``` + + +## Bearer 身份验证 + +首先在 `api.yml` 中定义一个 `Bearer` 身份验证方案: + +```yaml api.yml +auth: Bearer +auth-schemes: + Bearer: + scheme: bearer +``` + +这将生成一个 SDK,用户需要提供一个名为 `token` 的必需参数。 + +```ts index.ts +const client = new Client({ + token: "ey34..." +}) +``` + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yaml title="api.yml" {5-7} +auth: Bearer +auth-schemes: + Bearer: + scheme: bearer + token: + name: apiKey + env: PLANTSTORE_API_KEY +``` + +生成的 SDK 会是这样: + +```ts index.ts + +// 使用 process.env.PLANTSTORE_API_KEY +let client = new Client(); + +// token 已被重命名为 apiKey +client = new Client({ + apiKey: "ey34..." +}) +``` + +## 基本身份验证 + +首先在 `api.yml` 中定义一个 `Basic` 身份验证方案: + +```yaml api.yml +auth: Basic +auth-schemes: + Basic: + scheme: basic +``` + +这将生成一个 SDK,用户需要提供名为 `username` 和 `password` 的必需参数。 + +```ts index.ts +const client = new Client({ + username: "joeschmoe" + password: "ey34..." +}) +``` + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yaml title="api.yml" {5-11} +auth: Basic +auth-schemes: + Basic: + scheme: basic + username: + name: clientId + env: PLANTSTORE_CLIENT_ID + password: + name: clientSecret + env: PLANTSTORE_CLIENT_SECRET +``` + +生成的 SDK 会是这样: + +```ts index.ts + +// 使用 process.env.PLANTSTORE_CLIENT_ID 和 process.env.PLANTSTORE_CLIENT_SECRET +let client = new Client(); + +// 参数已被重命名 +client = new Client({ + clientId: "joeschmoe", + clientSecret: "ey34..." +}) +``` + +## 自定义头部(例如 API 密钥) + +你也可以使用自定义头部创建自己的身份验证方案。 + +```yaml title="api.yml" {3-5} +auth: ApiKeyAuthScheme +auth-schemes: + ApiKeyAuthScheme: + header: X-API-Key + type: string +``` + +这将生成一个 SDK,用户需要提供一个名为 `apiKey` 的必需参数。 + +```ts index.ts +const client = new Client({ + xApiKey: "ey34..." +}) +``` + +如果你想控制变量命名和要扫描的环境变量,请使用以下配置: + +```yaml title="api.yml" {7-8} +auth: ApiKeyAuthScheme +auth-schemes: + ApiKeyAuthScheme: + header: X-API-Key + type: string + name: apiKey + env: PLANTSTORE_API_KEY +``` + +生成的 SDK 会是这样: + +```ts index.ts + +// 使用 process.env.PLANTSTORE_API_KEY +let client = new Client(); + +// 参数已被重命名 +client = new Client({ + apiKey: "ey34..." +}) +``` + +## OAuth 客户端凭据 + + + +如果你的 API 使用 OAuth,你可以在 `api.yml` 中指定 oauth 方案,并在单独的 `auth.yml` 文件中定义令牌检索端点([示例](https://github.com/fern-api/fern/blob/3137938b70e058f3691ddef34d5c1cc29acc4b80/test-definitions/fern/apis/oauth-client-credentials/definition/api.yml))。 + +```yaml api.yml +name: api + +imports: + auth: auth.yml + +auth: OAuthScheme +auth-schemes: + OAuthScheme: + scheme: oauth + type: client-credentials + client-id-env: YOUR_CLIENT_ID + client-secret-env: YOUR_CLIENT_SECRET + get-token: + endpoint: auth.getTokenWithClientCredentials + request-properties: + client-id: $request.client_id # 格式: parameter-name: $request.property_name + client-secret: $request.client_secret # 格式: parameter-name: $request.property_name + response-properties: + access-token: $response.access_token # 格式: parameter-name: $response.property_name + expires-in: $response.expires_in # 格式: parameter-name: $response.property_name + +``` + +`request-properties` 和 `response-properties` 将 OAuth 标准参数映射到在 `auth.yml` 中定义的实际端点的请求和响应字段名称。 + + + 如果设置了 `expires-in` 属性,生成的 OAuth 令牌提供程序将在令牌过期时自动刷新令牌。否则,假设访问令牌永久有效。 + + +相应的 `auth.yml` 文件([示例](https://github.com/fern-api/fern/blob/3137938b70e058f3691ddef34d5c1cc29acc4b80/test-definitions/fern/apis/oauth-client-credentials/definition/auth.yml))定义了令牌端点: + +```yaml title="auth.yml" +types: + TokenResponse: + docs: | + OAuth 令牌响应。 + properties: + access_token: string + expires_in: integer + refresh_token: optional + +service: + auth: false + base-path: / + endpoints: + getTokenWithClientCredentials: + path: /token + method: POST + request: + name: GetTokenRequest + body: + properties: + client_id: string + client_secret: string + audience: literal<"https://api.example.com"> + grant_type: literal<"client_credentials"> + scope: optional + response: TokenResponse +``` + +如果你的 OAuth 服务器托管在与主 API 不同的 URL 上,你可以使用[每个环境多个 URL](/api-definitions/ferndef/api-yml/environments#multiple-urls-per-environment) 为身份验证和 API 调用指定单独的基础 URL。 + +有了这些,所有的 OAuth 逻辑都会在生成的 SDK 中自动处理。只要你配置了这些设置,你的客户端将自动检索访问令牌并根据需要刷新它。 + +在使用文档 playground 时,可以选择设置 `token-header` 和 `token-prefix` 来自定义头部键名和头部值前缀,以匹配 API 身份验证方案的预期格式。 + +例如,以下配置将产生头部 `Fern-Authorization: Fern-Bearer `: + +```yaml api.yml {5-6} +auth-schemes: + OAuthScheme: + scheme: oauth + type: client-credentials + token-header: Fern-Authorization + token-prefix: Fern-Bearer + get-token: + ... +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/availability.mdx b/fern/translations/zh/products/api-def/ferndef/availability.mdx new file mode 100644 index 000000000..edaa5f2d1 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/availability.mdx @@ -0,0 +1,103 @@ +--- +title: Fern Definition 中的可用性 +description: 向 Fern Definition API 服务、端点、类型或属性添加可用性标识,以指示其发布状态。 +sidebar-title: 可用性 +--- + +您可以在 Fern Definition 中为端点、类型或属性添加 `availability`。您可以在 [`docs.yml` 文件](/learn/docs/configuration/site-level-settings)中配置 API 参考文档各部分的 `availability`。 + +## 端点、类型和属性 + +可用性可以是: +- `in-development` 表示正在开发中;将显示 `Beta` 标签 +- `pre-release` 表示可用;将显示 `Beta` 标签 +- `deprecated` 表示将来会被移除;将显示 `Deprecated` 标签 +- `generally-available` 表示稳定且可供使用;将显示 `GA` 标签 + +### 端点 + + +```yaml {6} +service: + base-path: /pet + auth: true + endpoints: + add: + availability: deprecated + display-name: Add pet + docs: Add a new Pet to the store + method: POST + path: "" + request: AddPetRequest + response: Pet +``` + + +在 Fern Docs 中,这将显示为: + + +![Screenshot showing a deprecated tag next to an endpoint in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/endpoint-deprecated.png) + + +### 类型 + + +```yaml {15} + Pet: + properties: + id: + type: integer + docs: A unique ID for the Pet + name: + type: string + docs: The first name of the Pet + photoUrls: + type: list + docs: A list of publicly available URLs featuring the Pet + availability: generally-available + category: + type: optional + availability: pre-release + + Category: + properties: + id: optional + name: optional +``` + + +在 Fern Docs 中,这将显示为: + + +![Screenshot showing a beta tag next to a type in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/type-beta.png) + + +### 属性 + + +```yaml {12} + Pet: + properties: + id: + type: integer + docs: A unique ID for the Pet + name: + type: string + docs: The first name of the Pet + photoUrls: + type: list + docs: A list of publicly available URLs featuring the Pet + availability: deprecated + category: optional +``` + + +在 Fern Docs 中,这将显示为: + + +![Screenshot showing a deprecated tag next to a type's property in API Reference docs](https://fern-image-hosting.s3.amazonaws.com/property-deprecated.png) + + +## 部分 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/depending-on-other-apis.mdx b/fern/translations/zh/products/api-def/ferndef/depending-on-other-apis.mdx new file mode 100644 index 000000000..f5b091f9a --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/depending-on-other-apis.mdx @@ -0,0 +1,69 @@ +--- +title: "依赖其他 API" +subtitle: 导入 API 定义以生成统一的 SDK +sidebar-title: 依赖其他 API +--- + +Fern 允许您将其他 API 导入到您的 API 中。 + +这在以下情况下通常很有用: + +- 您希望在您的 API 中重用另一个 API 的类型 +- 您希望将多个微服务的 API 合并到一个 SDK 中(类似于 AWS SDK) + +## 注册依赖的 API + +第一步是**注册**您要依赖的 API。为此,使用 `register` 命令: + +``` +$ fern register +[some-dependency]: Uploading definition... +[some-dependency]: Registered @fern/some-dependency:0.0.1 +``` + +## 依赖已注册的 API + +要添加对另一个 API 的依赖,您必须添加一个 `dependencies.yml` 来声明您希望依赖的 API。 + + + + + + + + + + + + + +您的 `dependencies.yml` 包含您希望依赖的所有 API 列表: + +```yaml dependencies.yml +dependencies: + "@fern/some-dependency": "0.0.1" +``` + +接下来,您需要在您的 Fern 定义中创建一个文件夹来容纳依赖项。在文件夹内,创建一个特殊文件 `__package__.yml`,该文件指定您要添加的依赖项和版本。 + + + + + + + + + + + + + + + + +```yaml __package__.yml +export: + dependency: "@fern/some-dependency" +``` + +当您使用 `fern generate` 生成 SDK 时,`__package__.yml` 文件将有效地被您所依赖的 API 替换。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/endpoints.mdx b/fern/translations/zh/products/api-def/ferndef/endpoints.mdx new file mode 100644 index 000000000..b83cb83f6 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/endpoints.mdx @@ -0,0 +1,664 @@ +--- +title: Fern 定义中的端点 +description: 在 Fern 定义中将相关的 API 端点组织到服务中,并定义每个端点的 URL、HTTP 方法、请求、响应、错误等。 +sidebar-title: 端点 +--- + +在 Fern 中,您可以将相关的端点组织到一个**服务**中。这种分组方式提高了清晰度,使生成的 SDK 更加符合习惯。 + +## 服务定义 + +每个服务定义: + +1. 一个**基础路径**:所有端点 HTTP 路径的共同前缀 +2. 服务是否需要[身份验证](/learn/api-definitions/ferndef/authentication) +3. **端点** + + + ```yaml + service: + base-path: /users # 这定义了方法的组/命名空间 + auth: false + endpoints: {} + ``` + + + + 要定义空基础路径的服务,请使用空字符串:`base-path: ""` + + +### 章节显示名称 + +默认情况下,API 参考中的章节名称来源于服务文件名(例如,`user.yml` 变成 "User")。要覆盖章节的显示名称,[请在 `docs.yml` 中使用 `section` 属性](/docs/api-references/customize-api-reference-layout#renaming-sections)。 + +### SDK 方法名称 + +SDK 方法名称直接从服务文件名和端点键派生而来。文件名成为命名空间,端点键成为方法名称。 + +例如,给定以下定义: + + +```yaml +service: + base-path: /users + auth: false + endpoints: + create: + path: "" + method: POST + request: CreateUserRequest +``` + + +Fern 生成一个名为 `client.users.create()` 的方法。 + +## 端点 + +端点包括: + +- 一个 **URL 路径** _(可选择性地包括路径参数)_ +- 一个**显示名称** _(可选)_ +- 一个 **HTTP 方法** +- **请求信息** _(可选)_ + - **查询参数** + - **标头** + - **请求体** +- **成功(200)响应**信息 _(可选)_ +- 此端点可能返回的**错误(非 200)响应** _(可选)_ + +### URL 路径 + +每个端点都有一个 URL 路径。 + + +```yaml {6} +service: + base-path: /users + auth: false + endpoints: + getAllUsers: + path: /all + method: GET +``` + + +端点的完整路径是以下内容的连接: + +- [环境](/learn/api-definitions/ferndef/api-yml/environments) URL +- 服务 `base-path` +- 端点 `path` + +### 端点显示名称 + +显示名称将作为端点的标题出现。默认情况下,显示名称等于端点名称的"标题大小写"。如果您想要自定义端点名称,可以**设置显示名称**。 + +在下面的示例中,["Add a new plant to the store"](https://plantstore.dev/api-reference/plant-store-api/plants/add-plant) 显示为 API 参考中端点页面的标题。 + + +```yaml {7} +service: + base-path: /v3 + auth: false + endpoints: + addPlant: + path: /plant + display-name: Add a new plant to the store + method: POST +``` + + +### 路径参数 + +为您的端点提供路径参数以创建动态 URL。 + + +```yaml {6-8} +service: + base-path: /users + auth: false + endpoints: + getUser: + path: /{userId} + path-parameters: + userId: string + method: GET +``` + + +服务也可以有路径参数: + + + ```yaml {2-4} + service: + base-path: /projects/{projectId} + path-parameters: + projectId: string + auth: false + endpoints: + ... + ``` + + +### 查询参数 + +每个端点可以指定查询参数: + + +```yaml +service: + base-path: /users + auth: false + endpoints: + getAllUsers: + path: /all + method: GET + request: + # 符合习惯的 SDK 需要这个名称 + name: GetAllUsersRequest + query-parameters: + limit: optional +``` + + +#### `allow-multiple` + +使用 `allow-multiple` 指定查询参数在 URL 中允许多次出现,如 `?filter=jane&filter=smith`。这将修改生成的 SDK,以便使用者可以为查询参数提供多个值。 + + +```yaml {5} + ... + query-parameters: + filter: + type: string + allow-multiple: true +``` + + +### 身份验证 + +每个端点可以覆盖服务中指定的身份验证行为。 + + + ```yaml + service: + base-path: /users + auth: false + endpoints: + getMe: + path: "" + method: GET + # 这个端点将进行身份验证 + auth: true + docs: 根据 Authorization 标头返回当前用户。 + ``` + + +### 标头 + +每个端点可以指定请求标头: + + + ```yaml + service: + base-path: /users + auth: false + endpoints: + getAllUsers: + path: /all + method: GET + request: + # 符合习惯的 SDK 需要这个名称 + name: GetAllUsersRequest + headers: + X-Endpoint-Header: string + ``` + + +服务也可以指定请求标头。这些标头将级联到服务的端点。 + + + ```yaml {4-5} + service: + base-path: /users + auth: false + headers: + X-Service-Header: string + endpoints: + getAllUsers: + path: /all + method: GET + request: + # 符合习惯的 SDK 需要这个名称 + name: GetAllUsersRequest + headers: + X-Endpoint-Header: string + ``` + + +### 请求体 + +端点可以指定请求体类型。 + + +```yaml {10} +service: + base-path: /users + auth: false + endpoints: + setUserName: + path: /{userId}/set-name + path-parameters: + userId: string + method: POST + request: string +``` + + +#### 内联请求体 + +如果请求体是一个对象,您可以**内联类型声明**。这使生成的 SDK 更加符合习惯。 + + + ```yaml + service: + base-path: /users + auth: false + endpoints: + createUser: + path: /create + method: POST + request: + # 符合习惯的 SDK 需要这个名称 + name: CreateUserRequest + body: + properties: + userName: string + ``` + + +### 成功响应 + +端点可以指定 `response`,这是成功(200)调用时将返回的体的类型。 + + +```yaml +service: + base-path: /users + auth: false + endpoints: + getAllUsers: + path: /all + method: GET + response: list + +types: + User: + properties: + userId: string + name: string +``` + + +### 响应状态码 + +您还可以使用 `status-code` 字段为成功响应指定自定义状态码。 + + +```yaml {11} +service: + base-path: /users + auth: false + endpoints: + create: : + path: "" + method: POST + request: CreateUserRequest + response: + type: User + status-code: 201 + +types: + User: + properties: + userId: string + name: string +``` + + +### 分页 + +Fern 支持偏移量、游标、URI 和基于路径的分页方案。要在[生成的 SDK](/learn/sdks/deep-dives/auto-pagination) 中设置自动分页: + +1. 使用 `pagination` 字段标注所需的分页端点 +2. 指定分页方案(`offset`、`cursor`、`next_uri` 或 `next_path`) +3. 使用点访问表示法指定 `results` 的位置。 + + +在大多数偏移分页配置中包含 `step`,以确保偏移量按页面大小递增。当您的 API 为附加页面返回布尔指示符时,请使用 `has-next-page`。 + + + +```yaml title="偏移分页" {8-12} +service: + base-path: /users + auth: false + endpoints: + list: + path: "" + method: GET + pagination: + offset: $request.page + step: $request.page_size # 推荐 + results: $response.data + has-next-page: $response.has_more + request: + name: ListUsersRequest + query-parameters: + page: optional + page_size: optional + response: ListUsersResponse + +types: + ListUsersResponse: + properties: + data: list + has_more: boolean +``` + +```yaml title="游标分页" {8-11} +service: + base-path: /users + auth: false + endpoints: + list: + path: "" + method: GET + pagination: + cursor: $request.starting_after + next_cursor: $response.page.next.starting_after + results: $response.data + request: + name: ListUsersRequest + query-parameters: + starting_after: optional + response: ListUsersResponse + +types: + ListUsersResponse: + properties: + data: list + page: + properties: + next: + properties: + starting_after: optional +``` + +```yaml title="URI 分页" {8-10} +service: + base-path: /plants + auth: false + endpoints: + list: + path: "" + method: GET + pagination: + next_uri: $response.next_page_url + results: $response.data + response: ListPlantsResponse + +types: + ListPlantsResponse: + properties: + data: list + next_page_url: optional +``` + +```yaml title="路径分页" {8-10} +service: + base-path: /plants + auth: false + endpoints: + list: + path: "" + method: GET + pagination: + next_path: $response.next_page_path + results: $response.data + response: ListPlantsResponse + +types: + ListPlantsResponse: + properties: + data: list + next_page_path: optional +``` + + +`pagination` 字段支持以下属性: + + + +### 幂等端点 + +将端点标记为幂等,以允许 SDK 用户指定幂等性标头进行安全的请求重试。您还必须在 [`api.yml` 中配置幂等性标头](/learn/api-definitions/ferndef/api-yml/global-headers#idempotency-headers)来定义哪些标头可用。 + +```yaml title="service.yml" {8} +service: + base-path: /transactions + auth: true + endpoints: + send: + path: "" + method: POST + idempotent: true + request: SendTransactionRequest + response: Transaction +``` + +### 错误响应 + +端点可以指定错误响应,详细说明端点可能返回的非 200 响应。 + + +```yaml +service: + base-path: /users + auth: false + endpoints: + getUser: + path: /{userId} + path-parameters: + userId: string + method: GET + response: User + errors: + - UserNotFoundError + +types: + User: + properties: + userId: string + name: string + +errors: + UserNotFoundError: + status-code: 404 +``` + + +您可以在[错误](/learn/api-definitions/ferndef/errors)页面了解更多关于如何定义错误的信息。 + +## 指定示例 + +当您声明一个示例时,您也可以指定该端点可能如何使用的一些示例。编译器使用这些示例来增强生成的输出。示例将在您的 SDK、API 文档和 Postman 集合中显示为注释。 + +您可以为端点、类型和错误添加示例。 + + +```yaml {13-19} +service: + base-path: /users + auth: false + endpoints: + getUser: + path: /{userId} + path-parameters: + userId: string + method: GET + response: User + errors: + - UserNotFoundError + examples: + - path-parameters: + userId: alice-user-id + response: + body: + userId: alice-user-id + name: Alice + +types: + User: + properties: + userId: string + name: string + +errors: + UserNotFoundError: + status-code: 404 +``` + + +如果您要向端点添加示例,而该类型已经有示例,您可以使用 `$` 引用它。 +```yaml +service: + auth: true + base-path: /address + endpoints: + create: + method: POST + path: "" + request: CreateAddress + response: Address + examples: + - request: $CreateAddress.WhiteHouse + response: + body: $Address.WhiteHouseWithID + + CreateAddress: + properties: + street1: string + street2: optional + city: string + state: string + postalCode: string + country: string + isResidential: boolean + examples: + - name: WhiteHouse + value: + street1: 1600 Pennsylvania Avenue NW + city: Washington DC + state: Washington DC + postalCode: "20500" + country: US + isResidential: true + + Address: + extends: CreateAddress + properties: + id: + type: uuid + docs: The unique identifier for the address. + examples: + - name: WhiteHouseWithID + value: + id: 65ce514c-41e3-11ee-be56-0242ac120002 + street1: 1600 Pennsylvania Avenue NW + city: Washington DC + state: Washington DC + postalCode: "20500" + country: US + isResidential: true +```` + +示例包含端点调用的所有信息,包括请求体、路径参数、查询参数、标头和响应体。 + + + ```yaml + examples: + - path-parameters: + userId: some-user-id + query-parameters: + limit: 50 + headers: + X-My-Header: some-value + response: + body: + response-field: hello + ``` + + +#### 失败示例 + +您也可以指定失败的端点调用示例。向响应示例添加 `error` 属性来指定您正在演示的失败。 + + +```yaml {5} +examples: + - path-parameters: + userId: missing-user-id + response: + error: UserNotFoundError + +errors: + UserNotFoundError: + status-code: 404 +``` + + +如果错误有体,那么您必须在示例中包含该体。 + + +```yaml {6, 11} +examples: + - path-parameters: + userId: missing-user-id + response: + error: UserNotFoundError + body: "User with id `missing-user-id` was not found" + +errors: + UserNotFoundError: + status-code: 404 + type: string +``` + + +#### 从类型引用示例 + +为了避免重复,您可以使用 `$` 从类型引用示例。 + + +```yaml {12} +service: + base-path: /users + auth: true + endpoints: + getUser: + method: GET + path: /{userId} + path-parameters: + userId: UserId + examples: + - path-parameters: + userId: $UserId.Example1 + +types: + UserId: + type: integer + examples: + - name: Example1 + value: user-id-123 +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/endpoints/bytes.mdx b/fern/translations/zh/products/api-def/ferndef/endpoints/bytes.mdx new file mode 100644 index 000000000..444c61a9a --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/endpoints/bytes.mdx @@ -0,0 +1,56 @@ +--- +title: 二进制数据和文件 +subtitle: 使用 `bytes` 类型处理 API 中的二进制数据 +sidebar-title: 二进制数据和文件 +--- + + + + `bytes` 类型允许您在请求和响应中处理二进制数据。 + + +## 发送字节数据 + +如果您的 API 需要发送字节流(即通常用于音频、图像和其他文件等资源),那么您可以在 Fern Definition 中使用 `bytes` 类型来建模。 + +```yml audio.yml +service: + base-path: /audio + endpoints: + upload: + display-name: Upload audio + method: POST + path: /upload + content-type: application/octet-stream + request: + type: bytes + docs: The bytes of the MP3 file that you would like to upload +``` + +## 接收字节数据 + + + 在响应中处理二进制数据时,请使用 `type: file` 而不是 `type: bytes`。 + + +另一方面,如果您的 API 返回字节流,那么您可以利用 `bytes` 类型作为响应。 + +```yml textToSpeech.yml +service: + base-path: /tts + endpoints: + upload: + display-name: Upload audio + method: POST + path: "" + request: + name: TTSRequest + body: + properties: + text: + type: string + docs: The text that you want converted to speech. + response: + type: file + docs: The bytes of the audio file. +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/endpoints/multipart.mdx b/fern/translations/zh/products/api-def/ferndef/endpoints/multipart.mdx new file mode 100644 index 000000000..c3344dc9e --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/endpoints/multipart.mdx @@ -0,0 +1,48 @@ +--- +title: 多部分文件上传 +description: 使用 `multiform` 内容类型记录端点。 +sidebar-title: 多部分文件上传 +--- + + +在 Fern 中,端点定义在 `endpoints` 键下。如果您的端点请求包含文件上传,您可以使用 `file` 类型来指示请求是 `multiform` 内容类型。下面的示例演示了一个在请求体中包含文件的端点。 + + +```yaml {12} +service: + base-path: /documents + auth: false + endpoints: + uploadDocument: + path: /upload + method: POST + request: + name: UploadDocumentRequest + body: + properties: + file: file +``` + + +在给定的多部分请求中,带有 `format:binary` 的字符串参数将表示任意文件。 + +## 文件列表 + +如果您的端点支持文件列表,那么您的请求体必须如此指示。 + + +```yaml {12} +service: + base-path: /documents + auth: false + endpoints: + uploadDocuments: + path: /upload + method: POST + request: + name: UploadDocumentsRequest + body: + properties: + files: list +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/endpoints/rest.mdx b/fern/translations/zh/products/api-def/ferndef/endpoints/rest.mdx new file mode 100644 index 000000000..3e1bc9568 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/endpoints/rest.mdx @@ -0,0 +1,45 @@ +--- +title: HTTP JSON 端点 +sidebar-title: HTTP JSON 端点 +--- + + +Fern 中的端点定义在 `endpoints` 键下。以下是定义单个 REST 端点的示例: + +```yml title="users.yml" maxLines=0 +service: + base-path: /users + auth: false + endpoints: + createUser: + path: /create + method: POST + request: + body: + properties: + userName: string +``` + +## 示例 + +你可以通过使用 `examples` 键来提供请求和响应的示例。 + +```yaml {11-17} +service: + base-path: /users + auth: false + endpoints: + getUser: + path: /{userId} + path-parameters: + userId: string + method: GET + response: User + examples: + - path-parameters: + userId: alice-user-id + response: + body: + userId: alice-user-id + name: Alice +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/endpoints/sse.mdx b/fern/translations/zh/products/api-def/ferndef/endpoints/sse.mdx new file mode 100644 index 000000000..07dcfaed9 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/endpoints/sse.mdx @@ -0,0 +1,100 @@ +--- +title: 服务器发送事件和流式 API +subtitle: 使用 `response-stream` 键来建模流式端点 +sidebar-title: 服务器发送事件和流式 API +--- + + + + +在端点上指定 `response-stream` 允许您表示流式响应的端点。 + +## JSON 流式传输 + +如果您的 API 返回一系列 `JSON` 块,如下所示 + +```json +{ "text": "Hi, I am a" } +{ "text": "chatbot. Do you have any"} +{ "text": "questions for me"} +``` + +那么只需在端点的 `response-stream` 下指定响应即可。 + +```yaml title="chat.yml" {4} +service: + base-path: /chat + endpoints: + stream: + method: POST + path: "" + response-stream: Chat + +types: + Chat: + properties: + text: string +``` + +## 服务器发送事件 + +如果您的 API 返回服务器发送事件,带有 `data` 和 `event` 键,如下所示 + +```json +data: { "text": "Hi, I am a" } +data: { "text": "chatbot. Do you have any"} +data: { "text": "questions for me"} +``` + +那么请确保包含 `format: sse`。 + +```yaml title="chat.yml" {9} +service: + base-path: /chat + endpoints: + stream: + method: POST + path: "" + response-stream: + type: Chat + format: sse + +types: + Chat: + properties: + text: string +``` + +## `Stream` 参数 + +端点具有 `stream` 参数来控制响应是否流式传输已成为常见做法。Fern 以一等公民的方式支持此模式。 + +只需指定 `stream-condition` 以及普通响应和流式响应: + +```yaml title="chat.yml" {7} +service: + base-path: /chat + endpoints: + stream: + method: POST + path: "" + stream-condition: $request.stream + request: + name: StreamChatRequest + body: + properties: + stream: boolean + response: Chat + response-stream: + type: ChatChunk + format: sse + +types: + Chat: + properties: + text: string + tokens: integer + ChatChunk: + properties: + text: string +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/errors.mdx b/fern/translations/zh/products/api-def/ferndef/errors.mdx new file mode 100644 index 000000000..ba16cba5c --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/errors.mdx @@ -0,0 +1,26 @@ +--- +title: Fern Definition 中的错误 +description: 在 Fern Definition 中添加表示 API 端点失败响应的错误。 +sidebar-title: Fern Definition 中的错误 +--- + +错误表示端点的失败(非 200)响应。 + +一个错误包含: + +- HTTP 状态码 +- 主体类型 _(可选)_ + + +```yaml +errors: + UserNotFoundError: + status-code: 404 + type: UserNotFoundErrorBody + +types: + UserNotFoundErrorBody: + properties: + requestedUserId: string +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/examples.mdx b/fern/translations/zh/products/api-def/ferndef/examples.mdx new file mode 100644 index 000000000..f02496a9e --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/examples.mdx @@ -0,0 +1,368 @@ +--- +title: Fern 定义中的示例 +subtitle: 使用 Fern 定义添加 API 示例,这些示例会显示在 SDK 注释、API 参考文档和 Postman 集合中。 +sidebar-title: Fern 定义中的示例 +--- + +您可以为类型和端点添加示例。示例会显示为 SDK 中的注释、文档的请求和响应部分,以及 Postman 集合中。 + +## 验证 + +Fern CLI 会验证您的示例是否与预期的类型匹配。以下内容无法编译: + +```yaml +types: + UserId: + type: integer + examples: + - value: hello # 不是整数 +``` + +```bash CLI 错误消息 +[api]: example.yml -> types -> UserId -> examples[0] + 期望示例为整数。示例值为:"hello" +``` + +## 引用示例 + +您可以引用来自其他类型、端点或错误的示例。 + +就像类型一样,您可以组合示例。要引用来自其他类型的示例,请使用 `$`。 + +```yaml {14} +types: + UserId: + type: integer + examples: + - name: Example1 + value: user-id-123 + + User: + properties: + id: UserId + name: string + examples: + - value: + id: $UserId.Example1 + name: Jane Smith +``` + +## 类型示例 + +### 对象 + +```yml +types: + ShipTo: + properties: + street1: string + street2: optional + city: string + state: string + postalCode: string + country: Country + isResidential: boolean + examples: + - name: WhiteHouse + value: + street1: 1600 Pennsylvania Avenue NW + city: Washington DC + state: Washington DC + postalCode: "20500" + country: US + isResidential: true + - name: EmpireStateBuilding + value: + street1: 350 5th Ave + street2: Attn: Maintenance Department + city: New York + state: NY + postalCode: "10118" + country: US + isResidential: false +``` + + +```typescript +/** + * 代表一个邮寄地址。 + * + * 白宫地址 + * @example { + * street1: "1600 Pennsylvania Avenue NW", + * city: "Washington DC", + * state: "Washington DC", + * postalCode: "20500", + * country: "US", + * isResidential: true + * } + * + * * 帝国大厦地址 + * @example { + * street1: "350 5th Ave", + * street2: "Attn: Maintenance Department", + * city: "New York", + * state: "NY", + * postalCode: "10118", + * country: "US", + * isResidential: false + * } + */ +type ShipTo = { + street1: string; + street2?: string; + city: string; + state: string; + postalCode: string; + country: Country; + isResidential: boolean; +}; +``` + + +### 列表 + +```yml + Shipments: + type: list + examples: + - name: Default + value: + - status: "InTransit" + estimatedDeliveryDate: "2024-01-11" + - status: "Delivered" + estimatedDeliveryDate: "2024-01-13" +``` + +### 联合 + +#### 有区分的联合 + +```yml +types: + Animal: + union: + dog: Dog + cat: Cat + examples: + - value: + type: dog + likesToWoof: true + Dog: + properties: + likesToWoof: boolean + Cat: + properties: + likesToMeow: boolean +``` + + +```typescript +/** + * 代表一个动物,可以是狗或猫。 + * + * 狗的示例: + * @example { + * type: "dog", + * likesToWoof: true + * } + */ +type Animal = Dog | Cat; +``` + + +#### 无区分的联合 + +```yml +types: + Animal: + discriminated: false + union: + - Dog + - Cat + examples: + - value: + likesToMeow: true + Dog: + properties: + likesToWoof: boolean + Cat: + properties: + likesToMeow: boolean +``` + + +```typescript +/** + * 代表一个动物,可以是狗或猫。 + * + * 动物作为猫的示例: + * @example { + * likesToMeow: true + * } + */ +type Animal = Dog | Cat; +``` + + +### 别名 + +```yml +types: + UserId: + docs: 用户的唯一标识符 + type: string + examples: + - value: user-id-123 +``` + + + ```typescript + /** + * 用户的唯一标识符 * + * @example "user-id-123" + */ + type UserId = string; + ``` + + +## 端点示例 + +您可以为端点添加成功和错误响应的示例。示例可以引用类型的示例以避免重复。 + +```yml +service: + auth: true + base-path: "" + endpoints: + CreateShippingLabel: + docs: 创建新的运输标签。 + method: POST + path: /shipping + request: CreateShippingLabelRequest + response: ShippingLabel + errors: + - NotAuthorized + - InsufficientFunds + examples: + # 不引用其他示例的成功响应。 + - request: + orderId: "online_789" + weightInOunces: 5 + response: + body: + orderId: "online_789" + weightInOunces: 5 + trackingNumber: "1Z26W8370303469306" + price: 2.50 + + # 使用引用的成功响应。 + - request: $CreateShippingLabelRequest.SuccessfulRequest + response: + body: $ShippingLabel.Default + + # 错误响应。 + - request: $CreateShippingLabelRequest.InsufficientFundsRequest + response: + error: InsufficientFunds + body: $InsufficientFundsBody.Default + +types: + CreateShippingLabelRequest: + properties: + orderId: string + weightInOunces: integer + examples: + - name: SuccessfulRequest + value: + orderId: "online_123" + weightInOunces: 13 + - name: InsufficientFundsRequest + value: + orderId: "online_456" + weightInOunces: 2000 + + ShippingLabel: + properties: + orderId: string + weightInOunces: integer + trackingNumber: string + price: double + examples: + - name: Default + value: + orderId: "online_123" + weightInOunces: 13 + trackingNumber: "1Z12345E0205271688" + price: 12.35 + + InsufficientFundsBody: + properties: + message: string + examples: + - name: Default + value: + message: "Insufficient funds to create shipping label." + +errors: + NotAuthorized: + status-code: 401 + InsufficientFunds: + status-code: 422 + type: InsufficientFundsBody +``` + +### 带请求头的示例 + +当您在 `api.yml` 中定义了[全局请求头](/api-definitions/ferndef/api-yml/global-headers#global-headers)时,必须在示例中包含它们: + +```yml {15-16} +service: + auth: true + base-path: "" + endpoints: + CreateShippingLabel: + docs: 创建新的运输标签。 + method: POST + path: /shipping + request: CreateShippingLabelRequest + response: ShippingLabel + errors: + - NotAuthorized + - InsufficientFunds + examples: + - headers: + X-App-Id: "app_12345" + request: + orderId: "online_789" + weightInOunces: 5 + response: + body: + orderId: "online_789" + weightInOunces: 5 + trackingNumber: "1Z26W8370303469306" + price: 2.50 +``` + +## 路径参数示例 + +```yml +service: + auth: true + base-path: "" + endpoints: + TrackShipment: + docs: 跟踪货物的状态。 + method: GET + path: /shipping/{trackingNumber} + path-parameters: + trackingNumber: string + response: ShipmentStatus + examples: + - path-parameters: + trackingNumber: "1Z26W8370303469306" + response: + body: + status: "InTransit" + estimatedDeliveryDate: "2024-01-11" +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/export-openapi.mdx b/fern/translations/zh/products/api-def/ferndef/export-openapi.mdx new file mode 100644 index 000000000..3757bc618 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/export-openapi.mdx @@ -0,0 +1,19 @@ +--- +title: 从 Fern Definition 导出到 OpenAPI +description: 使用 fern export 命令将 Fern Definition 导出为 OpenAPI 3.1 格式。防止供应商锁定并轻松转换您的 API 定义。 +sidebar-title: 从 Fern Definition 导出到 OpenAPI +--- + +为了防止被锁定在 Fern Definition 格式中,您可以随时使用 [`fern export` 命令](/cli-api-reference/cli-reference/commands#fern-export)将您的 API 定义导出为 OpenAPI 3.1。 + +如果您在 `fern/apis/` 文件夹中定义了多个 API,请使用 `--api` 来指定要导出的 API。 + + + ```bash + fern export path/to/openapi.yml + fern export path/to/openapi.json + + # 指定要导出的 API + fern export --api public-api path/to/openapi.yml + ``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/imports.mdx b/fern/translations/zh/products/api-def/ferndef/imports.mdx new file mode 100644 index 000000000..e25c468c7 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/imports.mdx @@ -0,0 +1,23 @@ +--- +title: Fern 定义中的导入 +description: 使用导入功能从其他 Fern 定义文件中引用 API 类型和错误。 +sidebar-title: 导入 +--- + +导入功能允许您从其他文件中引用类型和错误。 + +```yaml title="person.yml" +types: + Person: ... +``` + +```yaml title="family.yml" +imports: + person: ./path/to/person.yml +types: + Family: + properties: + people: list # 使用导入的类型 +``` + +请注意,您只能导入存在于您的 Fern 定义中的文件(即,在同一个 `definition/` 文件夹中)。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/overview.mdx b/fern/translations/zh/products/api-def/ferndef/overview.mdx new file mode 100644 index 000000000..926f06e73 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/overview.mdx @@ -0,0 +1,103 @@ +--- +title: 什么是 Fern Definition? +description: Fern Definition 是用于 API 规范的 YAML 格式。在一个统一的真实数据源中定义您的 REST API 端点、数据模型和错误。 +sidebar-title: 什么是 Fern Definition? +--- + +Fern Definition 是一组 YAML 文件,是您 API 的单一真实数据源。您可以将 Fern Definition 提交到您的代码库中,其中描述了您的 API 请求、响应、模型、路径、方法、错误和身份验证方案。 + + + 想要使用 OpenAPI?没问题,我们[同样支持](/learn/api-definitions/overview/what-is-an-api-definition#openapi-swagger) + + +## Fern Definition 结构 + +要初始化一个 Fern Definition,只需运行: + +```sh +npm install -g fern-api +fern init +``` + +这将在您的项目中创建以下文件夹结构: + + + + + + + + + + + + +## Definition 文件 + +每个 Fern Definition 文件可以定义: + +- **[自定义类型](/learn/api-definitions/ferndef/types)**。使用**自定义类型**来构建您的数据模型。 +- **[端点](/learn/api-definitions/ferndef/endpoints/overview)**。一个**服务**是一组相关的 REST 端点。 +- **[错误](/learn/api-definitions/ferndef/errors)**。一个**错误**代表端点的失败(非 200)响应。 +- **[导入](/learn/api-definitions/ferndef/imports)**。使用**导入**在文件之间共享类型。 + +```yml imdb.yml maxLines=0 +service: + auth: false + base-path: /movies + endpoints: + createMovie: + docs: Add a movie to the database + method: POST + path: /create-movie + request: CreateMovieRequest + response: MovieId + + getMovie: + method: GET + path: /{movieId} + path-parameters: + movieId: MovieId + response: Movie + errors: + - NotFoundError + - UnauthorizedError + +types: + Movie: + properties: + title: string + rating: + type: double + docs: The rating scale from one to five stars + id: + type: MovieId + docs: The unique identifier for a movie + + CreateMovieRequest: + properties: + title: string + rating: double + +errors: + NotFoundError: + http: + statusCode: 404 + type: + properties: + id: MovieId + + UnauthorizedError: + http: + statusCode: 401 +``` + +## 为什么要使用另一种格式? + +Google 构建了 gRPC。Amazon 构建了 Smithy。Facebook 构建了 GraphQL。Palantir 构建了 Conjure。这些公司拒绝了 OpenAPI,而选择了更简洁的 API Definition Language。 + +我们构建 Fern 是为了将这种设计产品化,并使所有软件公司都能使用它。 + + + 尽管这是描述 API 的不同格式,**但您永远不会被锁定在 Fern 中**。将您的 [Fern Definition 转换为 OpenAPI](/learn/api-definitions/ferndef/export-openapi) 很容易。 + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/packages.mdx b/fern/translations/zh/products/api-def/ferndef/packages.mdx new file mode 100644 index 000000000..83dd37eda --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/packages.mdx @@ -0,0 +1,146 @@ +--- +title: Fern 定义中的包 +description: Fern 定义支持在包之间重用 API 类型和错误名称,并可以配置您的 API 文档结构。 +sidebar-title: 包 +--- + +## 什么是包? + +您的 API 定义中的每个文件夹都是一个包。 + + + + + + + + + + + + + + + +生成的 SDK 将匹配您的 API 定义层次结构。 + + +```ts +const client = new Client(); + +// 调用在 projects.yml 中定义的端点 +client.projects.get(); + +// 调用在 roles/admin.yml 中定义的端点 +client.roles.admin.get(); +``` + + +## 包配置 + +每个包都可以有一个名为 `__package__.yml` 的特殊定义文件。与任何其他定义文件一样,它可以包含[导入](/learn/api-definitions/ferndef/imports)、[类型](/learn/api-definitions/ferndef/types)、[端点](/learn/api-definitions/ferndef/endpoints/overview)和[错误](/learn/api-definitions/ferndef/errors)。 + +`__package__.yml` 中的端点将出现在包的根目录下。 +例如,以下生成的 SDK: + + +```ts +const client = new Client(); + +client.getProjects(); +``` + + +将具有一个 `fern/` 文件夹: + + + + + + + + + + + + +其中包含以下 `__package__.yml`: + + +```yaml +service: + base-path: "" + auth: false + endpoints: + getProjects: + method: GET + path: "" + response: list +``` + + +## 命名空间 + +每个包都有自己的命名空间。这意味着您可以在包之间重用类型名称和错误名称。 + +这在版本化您的 API 时很有用。例如,当您想要增加 API 版本时,可以将现有的 API 复制到新包中并开始进行更改。如果新的 API 版本重用了某些类型或错误,这是可以的,因为两个 API 位于不同的包中。 + + + + + + + + + + + + + + + + + + + +## 导航 + +`__package__.yml` 还允许您配置服务的导航顺序。当您想要控制文档的显示时,这是相关的。 + +例如,假设您有以下 `fern/` 文件夹: + + + + + + + + + + + + + +您的 API 将按字母顺序排序:projects、roles,然后是 users。如果您想控制导航,可以添加一个 `__package__.yml` 文件并配置顺序: + + + + + + + + + + + + + + + +```yaml +navigation: + - users.yml + - roles.yml + - projects.yml +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/types.mdx b/fern/translations/zh/products/api-def/ferndef/types.mdx new file mode 100644 index 000000000..f0609c4f3 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/types.mdx @@ -0,0 +1,396 @@ +--- +title: Fern 定义中的类型 +description: 类型描述了您的 API 的数据模型。Fern 具有许多内置类型,支持自定义类型,以及扩展和别名对象和联合类型。 +sidebar-title: 类型 +--- + +类型描述了您的 API 的数据模型。 + +## 内置类型 + +| 类型 | 描述 | +|------|-------------| +| `string` | 基本字符串类型 | +| `integer` | 整数类型 | +| `long` | 长整数类型 | +| `double` | 双精度浮点数 | +| `boolean` | 布尔值 true/false | +| `datetime` | [RFC 3339, section 5.6 datetime](https://ijmacd.github.io/rfc3339-iso8601/)。例如,`2017-07-21T17:32:28Z` | +| `date` | RFC 3339, section 5.6 日期 (YYYY-MM-DD)。例如,`2017-07-21` | +| `uuid` | UUID 标识符 | +| `base64` | Base64 编码数据 | +| `list` | 允许重复的有序集合,例如,`list` | +| `set` | 具有唯一元素的无序集合,例如,`set` | +| `map` | 键值映射,例如,`map` | +| `optional` | 可选值,例如,`optional` | +| `literal` | 字面值,例如,`literal<"Plants">` | +| `file` | 文件上传类型,例如,[文件上传](/learn/api-definitions/ferndef/endpoints/multipart) | +| `unknown` | 表示任意 JSON | + +## 自定义类型 + +在 Fern 中创建您自己的类型很容易! + +### 对象 + +最常见的自定义类型是**对象**。 + +在 Fern 中,您使用 `"properties"` 键来创建对象: + +```yaml {3,8} +types: + Person: + properties: + name: string + address: Address + + Address: + properties: + line1: string + line2: optional + city: string + state: string + zip: string + country: literal<"USA"> +``` + +这些表示 JSON 对象: + +```json +{ + "name": "Alice", + "address": { + "line1": "123 Happy Lane", + "city": "New York", + "state": "NY", + "zip": "10001", + "country": "USA" + } +} +``` + +您还可以使用 **extends** 来组合对象: + +```yaml {6} +types: + Pet: + properties: + name: string + Dog: + extends: Pet + properties: + breed: string +``` + +您可以扩展多个对象: + +```yaml {3-5} +types: + GoldenRetriever: + extends: + - Dog + - Pet + properties: + isGoodBoy: boolean +``` + +### 别名 + +别名类型是对现有类型的重命名。这通常是为了清晰起见。 + +```yaml +types: + # UserId 是 string 的别名 + UserId: string + + User: + properties: + id: UserId + name: string +``` + +### 枚举 + +枚举表示具有一组允许值的字符串。 + +在 Fern 中,您使用 `"enum"` 键来创建枚举: + +```yaml {3} +types: + WeatherReport: + enum: + - SUNNY + - CLOUDY + - RAINING + - SNOWING +``` + +枚举名称仅限于 `A-Z`、`a-z`、`0-9` 和 `_`,以确保生成的代码能够在 Fern 能够输出的所有语言中编译。如果您有一个不遵循此约定的枚举,您可以使用 `"name"` 键来指定自定义名称: + +```yaml +types: + Operator: + enum: + - name: LESS_THAN # <--- 将在 SDK 中使用的名称 + value: '<' # <--- 将被序列化的值 + - name: GREATER_THAN + value: '>' + - name: NOT_EQUAL + value: '!=' +``` + +### 判别联合 + +Fern 支持标记联合(也称为判别联合)。联合对于多态性很有用。这类似于 OpenAPI 中的 `oneOf` 概念。 + +在 Fern 中,您使用 `"union"` 键来创建联合: + +```yaml {3-5} +types: + Animal: + union: + dog: Dog + cat: Cat + Dog: + properties: + likesToWoof: boolean + Cat: + properties: + likesToMeow: boolean +``` + +在 JSON 中,联合有一个**判别属性**来区分联合的不同成员。默认情况下,Fern 使用 `"type"` 作为判别属性: + +```json +{ + "type": "dog", + "likesToWoof": true +} +``` + +您可以使用 "discriminant" 键自定义判别属性: + +```yaml {3} + types: + Animal: + discriminant: animalType + union: + dog: Dog + cat: Cat + Dog: + properties: + likesToWoof: boolean + Cat: + properties: + likesToMeow: boolean +``` + +这对应于如下的 JSON 对象: + +```json +{ + "animalType": "dog", + "likesToWoof": true +} +``` + +### 无判别联合 + +无判别联合类似于判别联合,但是您不需要定义显式的判别属性。 + +```yaml +MyUnion: + discriminated: false + union: + - string + - integer +``` + +### 泛型 + +Fern 支持浅泛型对象,以最小化代码重复。您可以定义一个泛型以供重用: + +```yaml +MySpecialMapItem: + properties: + key: Key, + value: Value, + diagnostics: string +``` + +现在,您可以将泛型类型实例化为类型别名: + +```yml +StringIntegerMapItem: + type: Response + +StringStringMapItem: + type: Response +``` + +您现在可以像使用任何其他类型一样自由使用这种类型!请注意,生成的代码不会使用泛型。上面的示例将在 TypeScript 中生成为: + +```typescript +type StringIntegerMapItem = { + key: string, + value: number, + diagnostics: string +} + +type StringStringMapItem = { + key: string, + value: string, + diagnostics: string +} +``` + +### 文档化类型 + +您可以为类型添加文档。这些文档被传递到编译器中,在生成的输出中非常有用(例如,SDK 中的文档字符串)。 + + +```yaml +types: + Person: + docs: 一个人代表一个人类 + properties: + name: string + age: + docs: 年龄(以年为单位) + type: integer +``` + + + +```typescript +/** + * 一个人代表一个人类 + */ +interface Person { + name: string; + // 年龄(以年为单位) + age: number; +} +``` + + +### 验证类型 + +您可以向类型(别名和引用)添加验证约束以确保数据完整性。这些验证约束存在于您的 API 定义中,由服务器强制执行,但生成的客户端 SDK 不包含验证逻辑。 + + +```yaml {8-11, 15-17} +types: + Person: + docs: 一个人代表一个人类 + properties: + name: + docs: 该人的全名 + type: string + validation: + minLength: 2 + maxLength: 100 + pattern: "^[A-Za-z ]+$" + age: + docs: 年龄(以年为单位) + type: integer + validation: + min: 0 + max: 150 +``` + + + + + +字符串类型支持几个验证约束。 + +```yaml {4-6, 11-13, 16-19} +types: + Word: + type: string + validation: + minLength: 2 + maxLength: 26 + User: + properties: + email: + type: string + validation: + format: email + maxLength: 254 + username: + type: string + validation: + minLength: 3 + maxLength: 20 + pattern: "^[a-zA-Z0-9_]+$" +``` + + + 所需的最小字符数 + + + + 允许的最大字符数 + + + + 字符串必须匹配的正则表达式模式 + + + + 字符串格式规范(例如,"email"、"uri"、"date-time") + + + + + + +数字类型(包括 `integer`、`long` 和 `double`)支持几个验证约束。 + +```yaml {4-6, 12-15, 18-20} +types: + Age: + type: integer + validation: + min: 0 + max: 150 + Product: + properties: + name: string + price: + type: double + validation: + min: 0 + exclusiveMin: true + multipleOf: 0.01 + quantity: + type: integer + validation: + min: 1 + max: 1000 +``` + + + 最小值(默认包含) + + + + 最大值(默认包含) + + + + 为 true 时,最小值为独占(值必须大于 min) + + + + 为 true 时,最大值为独占(值必须小于 max) + + + + 值必须是此数字的倍数 + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/webhooks.mdx b/fern/translations/zh/products/api-def/ferndef/webhooks.mdx new file mode 100644 index 000000000..1e3ec2ca4 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/webhooks.mdx @@ -0,0 +1,142 @@ +--- +title: Fern Definition 中的 Webhooks +description: 学习如何在 Fern Definition 中定义 webhooks +sidebar-title: Fern Definition 中的 Webhooks +max-toc-depth: 3 +--- + +在 Fern 中,您可以在 API 定义中指定 webhooks。这些 webhooks 将包含在生成的 SDK 和 API 文档中。 + +## Webhook 定义 + +每个 webhook 定义: + +1. **方法**:webhook 将使用的 HTTP 方法(`GET` 或 `POST`) +2. **标头**:webhook 将发送的标头 +3. **负载**:webhook 负载的模式 + + + ```yaml {2-10} + webhooks: + paymentNotification: + display-name: Payment Notification + docs: Receive a notification when a payment changes status + method: POST + headers: + X-Signature-Primary: + type: string + docs: An HMAC signature of the payload + payload: PaymentNotificationPayload + + types: + PaymentNotificationPayload: + discriminant: notificationType + union: + queued: QueuedPaymentNotification + processing: ProcessingPaymentNotification + completed: CompletedPaymentNotification + ``` + + +### 内联负载 + +您可以通过以下方式内联负载的模式: + + + ```yaml + webhooks: + paymentNotification: + display-name: Payment Notification + docs: Receive a notification when a payment changes status + method: POST + headers: + X-Signature-Primary: + type: string + docs: An HMAC signature of the payload + payload: + name: PaymentNotificationPayload + properties: + id: + type: string + docs: The notification id + amount: double + currency: Currency + ``` + + +## 生成 webhook 参考 + +Fern Docs 可以从您的定义自动生成 webhook 参考文档。在您的 `docs.yml` 文件中设置此配置。 + +您的 webhook 参考可以是单个文档页面: + +```yml docs.yml +navigation: + - api: Webhook Reference # Display name for this page + api-name: webhooks-v1 # Directory containing webhook definition +``` + +或者您可以为每个 webhook 事件配置单独的文档页面: + +```yaml title="docs.yml" +navigation: + - subpackage_api.newPlantWebhook # Format: subpackage_{name-of-api}.{webhook-event-name} +``` + +有关如何在 `docs.yml` 中配置 webhook 参考的更多信息,请参阅[生成您的 webhook 参考](/docs/api-references/generate-webhook-ref)。 + +## SDK 签名验证 + +您可以使用 `webhook-signature` 块直接在 Fern Definition 中配置 webhook 签名验证。配置后,Fern 生成 SDK 工具,允许用户验证 webhook 签名并确保事件来源于您的 API。 + +配置可以在**文档级别**(适用于所有 webhooks)或**每个 webhook**(覆盖文档级别的默认值)设置。两个级别都接受相同的配置选项。 + +Fern 支持两种验证方法:**HMAC**(使用共享密钥的对称密钥验证)和**非对称**(使用 RSA、ECDSA 或 Ed25519 密钥的公钥验证)。 + + + +```yaml title="api.yml" +webhook-signature: + type: hmac + header: x-webhook-signature + algorithm: sha256 + encoding: hex + +webhooks: + userCreated: + method: POST + payload: UserCreatedPayload + # Inherits document-level signature config + + orderCompleted: + method: POST + payload: OrderCompletedPayload + # Inherits document-level signature config +``` + + +```yaml title="api.yml" +webhook-signature: + type: hmac + header: x-webhook-signature + algorithm: sha256 + encoding: hex + +webhooks: + paymentNotification: + method: POST + payload: PaymentNotificationPayload + signature: + # Override: use asymmetric instead of HMAC + type: asymmetric + header: x-payment-signature + asymmetric-algorithm: rsa-sha256 + encoding: base64 + jwks-url: https://api.example.com/.well-known/jwks.json +``` + + + +### 配置选项 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/ferndef/websockets.mdx b/fern/translations/zh/products/api-def/ferndef/websockets.mdx new file mode 100644 index 000000000..18257c6e1 --- /dev/null +++ b/fern/translations/zh/products/api-def/ferndef/websockets.mdx @@ -0,0 +1,100 @@ +--- +title: Fern Definition 中的 WebSockets +description: 了解如何在 Fern Definition 中定义 WebSockets +sidebar-title: WebSockets +--- + +WebSockets 使用户能够与服务器创建连接,通过该连接可以发送双向通信。 + +在 Fern 中,您可以在 API 定义中指定 WebSockets。WebSockets 将包含在生成的 SDK 和 API 文档中。 + +## WebSocket 定义 +每个 WebSocket 都在自己的文件中定义,通过 `channel` 对象进行描述。将您的 WebSocket 定义文件放在 `fern` 文件夹中,与其他 API 定义文件一起。 + + + + + + + + + + + + +### channel 对象 + +`channel` 由以下字段定义: + +- `auth`:WebSocket 的身份验证方案 +- `path`:WebSocket 的路径 +- `headers` _(可选)_:WebSocket 将发送的任何标头 +- `path-parameters` _(可选)_:WebSocket 路径中的任何路径参数 +- `query-parameters` _(可选)_:WebSocket 初始请求中使用的任何查询参数 +- `messages` _(可选)_:WebSocket 连接后可以发送和接收的消息模式 + - `origin`:发送消息的实体(例如 `client` 或 `server`) + - `body`:消息的模式 +- `examples`:示例 WebSocket 连接 _(可选)_ + +### WebSocket 示例 + + + ```yaml + channel: + path: /chat + auth: false + query-parameters: + model_id: + type: optional + docs: The unique identifier of the model. + model_version: + type: optional + docs: The version number of the model. + messages: + publish: + origin: client + body: PublishEvent + subscribe: + origin: server + body: SubscribeEvent + examples: + - query-parameters: + model_id: "123" + messages: + - type: publish + body: + text: "Hello, world." + - type: subscribe + body: + id: "23823049" + message: "Hello there, how are you?" + types: + PublishEvent: + docs: The input from the user to send through the WebSocket. + properties: + text: + type: string + docs: The user text to send into the conversation. + SubscribeEvent: + docs: The response from the server sent through the WebSocket. + properties: + id: + type: string + docs: The id of the message. + message: + type: string + docs: The message sent through the socket. + ``` + + +## WebSocket API 参考 + +### WebSocket 参考 + +Fern 为 WebSockets 渲染[独特的参考页面](/learn/docs/api-references/generate-websocket-ref)。**握手**部分概述了与服务器连接的协议,而**发送**和**接收**部分概述了客户端和服务器之间可以发送的消息模式。 + +用户可以直接在 API 参考中连接和使用 WebSockets(查看 Hume 的 WebSockets [这里](https://dev.hume.ai/reference/text-to-speech-tts/stream-input))。 + + +WebSocket 参考 + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/auth.mdx b/fern/translations/zh/products/api-def/grpc/auth.mdx new file mode 100644 index 000000000..daee6709e --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/auth.mdx @@ -0,0 +1,371 @@ +--- +title: 身份验证 +subtitle: 为 gRPC 服务配置身份验证,包括 TLS、JWT 和自定义认证 +sidebar-title: 身份验证 +--- + +gRPC 支持各种身份验证机制来保护您的服务。身份验证可以在传输层(TLS)和应用层(凭证、令牌等)进行配置。 + +## 传输安全(TLS) + +gRPC 强烈建议在生产环境服务中使用 TLS 以确保加密通信: + +```protobuf auth_service.proto +syntax = "proto3"; + +package auth.v1; + +// Authentication service +service AuthService { + // Authenticate user and return JWT token + rpc Login(LoginRequest) returns (LoginResponse); + + // Validate and refresh JWT token + rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse); + + // Logout and invalidate token + rpc Logout(LogoutRequest) returns (google.protobuf.Empty); +} + +message LoginRequest { + string email = 1; + string password = 2; +} + +message LoginResponse { + string access_token = 1; + string refresh_token = 2; + int64 expires_in = 3; + User user = 4; +} +``` + +在您的服务器中配置 TLS: + +```python title="server.py" +import grpc +from grpc import ssl_channel_credentials +import auth_service_pb2_grpc + +def create_secure_server(): + # Load TLS credentials + server_credentials = grpc.ssl_server_credentials([ + (private_key, certificate_chain) + ]) + + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + auth_service_pb2_grpc.add_AuthServiceServicer_to_server( + AuthServiceServicer(), server + ) + + # Listen on secure port + server.add_secure_port('[::]:443', server_credentials) + return server +``` + +## JWT 身份验证 + +使用 JWT 令牌进行无状态身份验证: + +```protobuf auth.proto +syntax = "proto3"; + +package auth.v1; + +// JWT claims for user authentication +message JWTClaims { + string user_id = 1; + string email = 2; + repeated string roles = 3; + google.protobuf.Timestamp issued_at = 4; + google.protobuf.Timestamp expires_at = 5; +} +``` + +在您的服务中实现 JWT 身份验证: + +```python title="auth_service.py" +import grpc +import jwt +from grpc import ServicerContext + +class AuthServiceServicer(auth_service_pb2_grpc.AuthServiceServicer): + + def Login(self, request, context): + # Validate credentials + user = self.validate_credentials(request.email, request.password) + if not user: + context.set_code(grpc.StatusCode.UNAUTHENTICATED) + context.set_details('Invalid credentials') + return auth_service_pb2.LoginResponse() + + # Generate JWT token + payload = { + 'user_id': user.id, + 'email': user.email, + 'roles': user.roles, + 'exp': datetime.utcnow() + timedelta(hours=1) + } + + access_token = jwt.encode(payload, JWT_SECRET, algorithm='HS256') + + return auth_service_pb2.LoginResponse( + access_token=access_token, + refresh_token=self.generate_refresh_token(user.id), + expires_in=3600, + user=user + ) +``` + +## 用于身份验证的拦截器 + +使用 gRPC 拦截器来处理所有方法的身份验证: + +```python title="auth_interceptor.py" +import grpc +import jwt +from grpc import ServicerContext + +class AuthInterceptor(grpc.ServerInterceptor): + + def __init__(self, jwt_secret, exempt_methods=None): + self.jwt_secret = jwt_secret + self.exempt_methods = exempt_methods or [] + + def intercept_service(self, continuation, handler_call_details): + method_name = handler_call_details.method + + # Skip authentication for exempt methods + if method_name in self.exempt_methods: + return continuation(handler_call_details) + + # Extract metadata + metadata = dict(handler_call_details.invocation_metadata) + authorization = metadata.get('authorization', '') + + if not authorization.startswith('Bearer '): + return self._unauthenticated_response() + + token = authorization[7:] # Remove 'Bearer ' prefix + + try: + # Validate JWT token + payload = jwt.decode(token, self.jwt_secret, algorithms=['HS256']) + + # Add user info to context + handler_call_details = handler_call_details._replace( + invocation_metadata=handler_call_details.invocation_metadata + ( + ('user_id', payload['user_id']), + ('user_email', payload['email']), + ) + ) + + return continuation(handler_call_details) + + except jwt.ExpiredSignatureError: + return self._expired_token_response() + except jwt.InvalidTokenError: + return self._invalid_token_response() + + def _unauthenticated_response(self): + def abort(ignored_request, context): + context.set_code(grpc.StatusCode.UNAUTHENTICATED) + context.set_details('Missing or invalid authorization header') + return grpc.unary_unary_rpc_method_handler(abort) +``` + +## API 密钥身份验证 + +实现基于 API 密钥的身份验证: + +```protobuf api_key.proto +syntax = "proto3"; + +package auth.v1; + +message ApiKeyRequest { + string api_key = 1; + string service_name = 2; +} + +message ApiKeyResponse { + bool valid = 1; + string client_id = 2; + repeated string permissions = 3; + google.protobuf.Timestamp expires_at = 4; +} +``` + +服务器实现: + +```python title="api_key_auth.py" +class ApiKeyAuthInterceptor(grpc.ServerInterceptor): + + def __init__(self, api_key_store): + self.api_key_store = api_key_store + + def intercept_service(self, continuation, handler_call_details): + metadata = dict(handler_call_details.invocation_metadata) + api_key = metadata.get('x-api-key', '') + + if not api_key: + return self._unauthorized_response('API key required') + + # Validate API key + key_info = self.api_key_store.get(api_key) + if not key_info or key_info.is_expired(): + return self._unauthorized_response('Invalid or expired API key') + + # Add client info to context + handler_call_details = handler_call_details._replace( + invocation_metadata=handler_call_details.invocation_metadata + ( + ('client_id', key_info.client_id), + ('permissions', ','.join(key_info.permissions)), + ) + ) + + return continuation(handler_call_details) +``` + +## OAuth2 集成 + +与 OAuth2 提供商集成: + +```python title="oauth2_auth.py" +import requests +from google.oauth2 import id_token +from google.auth.transport import requests as google_requests + +class OAuth2Interceptor(grpc.ServerInterceptor): + + def __init__(self, google_client_id): + self.google_client_id = google_client_id + + def intercept_service(self, continuation, handler_call_details): + metadata = dict(handler_call_details.invocation_metadata) + auth_header = metadata.get('authorization', '') + + if not auth_header.startswith('Bearer '): + return self._unauthorized_response() + + token = auth_header[7:] + + try: + # Verify Google ID token + idinfo = id_token.verify_oauth2_token( + token, google_requests.Request(), self.google_client_id + ) + + # Add user info to context + handler_call_details = handler_call_details._replace( + invocation_metadata=handler_call_details.invocation_metadata + ( + ('user_id', idinfo['sub']), + ('user_email', idinfo['email']), + ('user_name', idinfo.get('name', '')), + ) + ) + + return continuation(handler_call_details) + + except ValueError: + return self._invalid_token_response() +``` + +## 客户端身份验证 + +在客户端配置身份验证: + +```python title="client.py" +import grpc + +# TLS with JWT +def create_authenticated_channel(server_address, jwt_token): + credentials = grpc.ssl_channel_credentials() + channel = grpc.secure_channel(server_address, credentials) + + # Add JWT token to all requests + def jwt_interceptor(continuation, client_call_details): + metadata = list(client_call_details.metadata or []) + metadata.append(('authorization', f'Bearer {jwt_token}')) + + client_call_details = client_call_details._replace(metadata=metadata) + return continuation(client_call_details) + + intercepted_channel = grpc.intercept_channel(channel, jwt_interceptor) + return intercepted_channel + +# API Key authentication +def create_api_key_channel(server_address, api_key): + credentials = grpc.ssl_channel_credentials() + channel = grpc.secure_channel(server_address, credentials) + + def api_key_interceptor(continuation, client_call_details): + metadata = list(client_call_details.metadata or []) + metadata.append(('x-api-key', api_key)) + + client_call_details = client_call_details._replace(metadata=metadata) + return continuation(client_call_details) + + intercepted_channel = grpc.intercept_channel(channel, api_key_interceptor) + return intercepted_channel +``` + +## 基于角色的访问控制 + +实现用于细粒度权限控制的 RBAC: + +```protobuf rbac.proto +syntax = "proto3"; + +package auth.v1; + +message Permission { + string resource = 1; + string action = 2; +} + +message Role { + string name = 1; + repeated Permission permissions = 2; +} + +message UserRoles { + string user_id = 1; + repeated string role_names = 2; +} +``` + +RBAC 拦截器实现: + +```python title="rbac_interceptor.py" +class RBACInterceptor(grpc.ServerInterceptor): + + def __init__(self, permission_store): + self.permission_store = permission_store + + def intercept_service(self, continuation, handler_call_details): + # Get user info from context (added by auth interceptor) + metadata = dict(handler_call_details.invocation_metadata) + user_id = metadata.get('user_id') + + if not user_id: + return self._unauthorized_response() + + # Check permissions for the method + method_name = handler_call_details.method + required_permission = self._get_required_permission(method_name) + + if required_permission and not self._has_permission(user_id, required_permission): + return self._forbidden_response() + + return continuation(handler_call_details) + + def _has_permission(self, user_id, permission): + user_roles = self.permission_store.get_user_roles(user_id) + for role in user_roles: + if permission in role.permissions: + return True + return False +``` + +gRPC 的灵活身份验证系统允许您实现安全、可扩展的身份验证模式,这些模式可以在不同的环境和用例中工作。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/automation.mdx b/fern/translations/zh/products/api-def/grpc/automation.mdx new file mode 100644 index 000000000..5c4f38a51 --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/automation.mdx @@ -0,0 +1,512 @@ +--- +title: 同步你的 gRPC 规范 +description: 使用 Fern 自动化 gRPC 规范同步。设置 GitHub Actions、CI/CD 流水线和定期更新 Protocol Buffer 文件。 +sidebar-title: 同步 gRPC 规范 +--- + +保持你的 gRPC 规范与代码库同步对于维护准确的 SDK 和文档至关重要。Fern 提供了多种自动化选项来简化这个过程。 + +## GitHub Actions + +使用 Fern 的 GitHub Action 在你的 Protocol Buffer 文件发生变化时自动更新 SDK 和文档。 + + + +```yaml title=".github/workflows/fern.yml" +name: Fern + +on: + push: + branches: + - main + paths: + - 'proto/**/*.proto' + - 'fern/**/*.yml' + pull_request: + branches: + - main + paths: + - 'proto/**/*.proto' + - 'fern/**/*.yml' + +jobs: + fern-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Check gRPC spec + uses: fern-api/action@v0 + with: + command: check + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + + fern-generate: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + # Required if using local-generation: true + - name: Setup buf + uses: bufbuild/buf-setup-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate SDKs and docs + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## Protocol buffer 验证 + +在生成 SDK 之前验证、检查并检测 Protocol Buffer 文件中的破坏性变更。 + +```yaml title=".github/workflows/proto-validation.yml" +name: Protocol Buffer Validation + +on: + push: + paths: + - 'proto/**/*.proto' + - 'buf.yaml' + pull_request: + paths: + - 'proto/**/*.proto' + +jobs: + validate-proto: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Protocol Buffer Compiler + uses: arduino/setup-protoc@v2 + with: + version: '23.4' + + - name: Setup buf + uses: bufbuild/buf-setup-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Lint Protocol Buffers + run: buf lint + + - name: Validate Protocol Buffer files + run: | + find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \; + + - name: Check for breaking changes + run: buf breaking --against '.git#branch=main' + + - name: Generate and validate with Fern + uses: fern-api/action@v0 + with: + command: check + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + + + +如果你正在使用 [Buf Schema Registry](https://buf.build/product/bsr),可以添加一个步骤来发布你的模式: + +```yaml title=".github/workflows/buf-sync.yml" +name: Buf Sync + +on: + push: + paths: + - 'proto/**/*.proto' + - 'buf.yaml' + +jobs: + buf-sync: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup buf + uses: bufbuild/buf-setup-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Lint Protocol Buffers + run: buf lint + + - name: Check for breaking changes + run: buf breaking --against '.git#branch=main' + + - name: Generate and push to Buf Registry + run: | + buf generate + buf push + env: + BUF_TOKEN: ${{ secrets.BUF_TOKEN }} + + - name: Generate SDKs with Fern + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + + + +可选择创建一个 `buf.yaml` 文件来自定义 buf 的检查规则、破坏性变更检测和依赖项: + +```yaml title="buf.yaml" +version: v1 +deps: + - buf.build/googleapis/googleapis + - buf.build/envoyproxy/protoc-gen-validate +lint: + use: + - DEFAULT + except: + - UNARY_RPC +breaking: + use: + - FILE +``` + + + +## 从源码自动同步 + +配置 Fern 自动从各种源拉取 Protocol Buffer 文件: + +### 从 git 仓库 +```yaml title="generators.yml" {3-7} +api: + specs: + - spec: + git: + repository: https://github.com/your-org/proto-definitions + path: services/user_service.proto + branch: main + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +### 从本地目录 +```yaml title="generators.yml" {3-4} +api: + specs: + - spec: proto/user_service.proto + auto-sync: true + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +## CI/CD 集成 + +将 Fern 集成到现有的 CI/CD 流水线中,自动生成 SDK 和文档。 + +### CircleCI + + + +```yaml title=".circleci/config.yml" {15-26} +version: 2.1 + +orbs: + fern: fernapi/fern@1.0 + +workflows: + version: 2 + build-and-generate: + jobs: + - build + - test: + requires: + - build + - validate-proto: + requires: + - build + - fern/generate: + requires: + - test + - validate-proto + filters: + branches: + only: main + context: + - fern-context + +jobs: + validate-proto: + docker: + - image: namely/protoc-all:1.51_1 + steps: + - checkout + # Required if using local-generation: true + - run: + name: Install buf + command: | + curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf + chmod +x /usr/local/bin/buf + - run: + name: Validate Protocol Buffers + command: | + find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \; +``` + +### GitLab CI + + + +```yaml title=".gitlab-ci.yml" {13-25} +stages: + - build + - test + - validate + - generate + +variables: + FERN_TOKEN: $FERN_TOKEN + +build: + stage: build + script: + - echo "Building gRPC service..." + +validate-proto: + stage: validate + image: namely/protoc-all:1.51_1 + script: + - find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \; + only: + changes: + - proto/**/*.proto + +generate-sdks: + stage: generate + image: fernapi/fern:latest + # Required if using local-generation: true + before_script: + - curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf + - chmod +x /usr/local/bin/buf + script: + - fern generate + only: + - main +``` + +## 定期更新 + +设置定期更新以确保你的 SDK 保持最新: + +```yaml title=".github/workflows/scheduled-update.yml" +name: Scheduled gRPC Update + +on: + schedule: + - cron: '0 2 * * 1' # Every Monday at 2 AM UTC + workflow_dispatch: + +jobs: + update-proto: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Sync Protocol Buffer files + run: | + # Sync from upstream proto repository + git subtree pull --prefix=proto https://github.com/your-org/proto-definitions main --squash + + - name: Generate with latest spec + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + + - name: Create PR if changes + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: update Protocol Buffer definitions" + title: "Update Protocol Buffer definitions" + body: "Automated update of Protocol Buffer definitions from upstream repository" +``` + +## 从 gRPC 服务器生成代码 + +对于可以生成自己的 Protocol Buffer 定义的服务器: + +```yaml title=".github/workflows/auto-generate.yml" +name: Auto-generate from gRPC server + +on: + push: + paths: + - 'src/**/*.py' # Trigger on server code changes + - 'src/**/*.go' + - 'src/**/*.java' + +jobs: + generate-proto: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup environment + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install dependencies + run: | + pip install grpcio-tools + + - name: Generate Protocol Buffer files + run: | + python -m grpc_tools.protoc \ + --proto_path=src/protos \ + --python_out=. \ + --grpc_python_out=. \ + --descriptor_set_out=proto/service.protoset \ + src/protos/*.proto + + - name: Convert to Protocol Buffer text format + run: | + protoc --decode_raw < proto/service.protoset > proto/user_service.proto + + - name: Generate SDKs + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## 监控变更 + +跟踪你的 Protocol Buffer 规范的变更: + +```yaml title="generators.yml" {4-9} +api: + specs: + - spec: proto/user_service.proto + change-detection: + enabled: true + breaking-changes: error + notifications: + slack: ${{ secrets.SLACK_WEBHOOK }} + email: team@yourcompany.com + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +## 多服务同步 + +为不同组件同步不同的 Protocol Buffer 服务: + +```yaml title="generators.yml" {3-7, 12-16, 21-25} +environments: + user-service: + specs: + - spec: proto/user_service.proto + overlays: + - user-service-overlay.yml + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@yourcompany/user-service-sdk" + order-service: + specs: + - spec: proto/order_service.proto + overlays: + - order-service-overlay.yml + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@yourcompany/order-service-sdk" + payment-service: + specs: + - spec: proto/payment_service.proto + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@yourcompany/payment-service-sdk" +``` + +## gRPC 反射同步 + +自动从启用了服务器反射的运行中的 gRPC 服务同步 Protocol Buffer 定义: + +```python title="scripts/sync_from_reflection.py" +import grpc +from grpc_reflection.v1alpha import reflection_pb2 +from grpc_reflection.v1alpha import reflection_pb2_grpc +import subprocess + +def sync_from_grpc_reflection(server_address, output_dir): + """从 gRPC 反射同步 Protocol Buffer 定义""" + + channel = grpc.insecure_channel(server_address) + reflection_stub = reflection_pb2_grpc.ServerReflectionStub(channel) + + # 列出服务 + request = reflection_pb2.ServerReflectionRequest( + list_services="" + ) + + response = reflection_stub.ServerReflectionInfo(iter([request])) + + for resp in response: + if resp.HasField('list_services_response'): + for service in resp.list_services_response.service: + print(f"Found service: {service.name}") + + # 获取服务的文件描述符 + file_request = reflection_pb2.ServerReflectionRequest( + file_containing_symbol=service.name + ) + + file_response = reflection_stub.ServerReflectionInfo(iter([file_request])) + + for file_resp in file_response: + if file_resp.HasField('file_descriptor_response'): + # 保存描述符到文件 + descriptor_path = f"{output_dir}/{service.name}.protoset" + with open(descriptor_path, 'wb') as f: + f.write(file_resp.file_descriptor_response.file_descriptor_proto[0]) + + # 转换为文本格式 + proto_path = f"{output_dir}/{service.name}.proto" + subprocess.run([ + 'protoc', + '--decode_raw', + '--proto_path', output_dir, + descriptor_path + ], stdout=open(proto_path, 'w')) + +if __name__ == "__main__": + sync_from_grpc_reflection("localhost:50051", "proto/") +``` + +这确保了你的 gRPC 服务的任何变更都能自动反映在你的 SDK 和文档中,在整个 API 生态系统中保持一致性。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/overview.mdx b/fern/translations/zh/products/api-def/grpc/overview.mdx new file mode 100644 index 000000000..03286954b --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/overview.mdx @@ -0,0 +1,240 @@ +--- +title: 什么是 gRPC? +description: gRPC 是一个使用 Protocol Buffers 的高性能 RPC 框架 +sidebar-title: 什么是 gRPC? +--- + + + + + + Fern 目前仅支持为 .NET/C# 生成 gRPC SDK。Protobuf 规范可用于所有语言的文档生成。[提交问题](https://github.com/fern-api/fern/issues)请求支持其他语言。 + + +gRPC 是一个现代的、开源的、高性能的远程过程调用(RPC)框架,可以在任何环境中运行。它使用 Protocol Buffers (protobuf) 作为接口定义语言,并支持多种编程语言。 +Fern 兼容 gRPC 服务,可以从您的 `.proto` 文件生成 SDK 和文档。 + +要查看实际示例,请参见 [gRPC 演示文档](https://grpc-demo.docs.buildwithfern.com/) 及其 [GitHub 仓库](https://github.com/fern-demo/grpc-demo)。 + +以下是一个 gRPC 服务定义的示例: + +```protobuf user_service.proto +syntax = "proto3"; + +package userservice.v1; + +// User service for managing user accounts +service UserService { + // Create a new user account + rpc CreateUser(CreateUserRequest) returns (User); + + // Get user by ID + rpc GetUser(GetUserRequest) returns (User); + + // List users with pagination + rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); + + // Update user information + rpc UpdateUser(UpdateUserRequest) returns (User); + + // Delete a user account + rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty); + + // Stream user events (server streaming) + rpc StreamUserEvents(StreamUserEventsRequest) returns (stream UserEvent); + + // Upload user data (client streaming) + rpc UploadUserData(stream UploadUserDataRequest) returns (UploadUserDataResponse); + + // Real-time chat (bidirectional streaming) + rpc Chat(stream ChatMessage) returns (stream ChatMessage); +} + +// Messages for user operations +message User { + string id = 1; + string email = 2; + string name = 3; + int32 age = 4; + google.protobuf.Timestamp created_at = 5; + UserStatus status = 6; +} + +message CreateUserRequest { + string email = 1; + string name = 2; + int32 age = 3; +} + +message GetUserRequest { + string id = 1; +} + +message ListUsersRequest { + int32 page_size = 1; + string page_token = 2; + string filter = 3; +} + +message ListUsersResponse { + repeated User users = 1; + string next_page_token = 2; + int32 total_count = 3; +} + +message UpdateUserRequest { + string id = 1; + User user = 2; + google.protobuf.FieldMask update_mask = 3; +} + +message DeleteUserRequest { + string id = 1; +} + +message StreamUserEventsRequest { + string user_id = 1; + repeated UserEventType event_types = 2; +} + +message UserEvent { + string id = 1; + string user_id = 2; + UserEventType type = 3; + google.protobuf.Timestamp timestamp = 4; + google.protobuf.Any data = 5; +} + +message UploadUserDataRequest { + oneof data { + UserDataChunk chunk = 1; + UserDataMetadata metadata = 2; + } +} + +message UserDataChunk { + bytes data = 1; + int64 offset = 2; +} + +message UserDataMetadata { + string filename = 1; + int64 total_size = 2; + string content_type = 3; +} + +message UploadUserDataResponse { + string file_id = 1; + int64 bytes_uploaded = 2; +} + +message ChatMessage { + string id = 1; + string user_id = 2; + string room_id = 3; + string content = 4; + google.protobuf.Timestamp timestamp = 5; + ChatMessageType type = 6; +} + +// Enums +enum UserStatus { + USER_STATUS_UNSPECIFIED = 0; + USER_STATUS_ACTIVE = 1; + USER_STATUS_INACTIVE = 2; + USER_STATUS_SUSPENDED = 3; +} + +enum UserEventType { + USER_EVENT_TYPE_UNSPECIFIED = 0; + USER_EVENT_TYPE_CREATED = 1; + USER_EVENT_TYPE_UPDATED = 2; + USER_EVENT_TYPE_DELETED = 3; + USER_EVENT_TYPE_LOGIN = 4; + USER_EVENT_TYPE_LOGOUT = 5; +} + +enum ChatMessageType { + CHAT_MESSAGE_TYPE_UNSPECIFIED = 0; + CHAT_MESSAGE_TYPE_TEXT = 1; + CHAT_MESSAGE_TYPE_IMAGE = 2; + CHAT_MESSAGE_TYPE_FILE = 3; + CHAT_MESSAGE_TYPE_SYSTEM = 4; +} +``` + +## 设置您的 fern 文件夹 + + 需要帮助开始使用 gRPC 和 Fern?在[这里](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)获得实时支持 + + + + +在您的项目根目录中创建一个 `fern/` 文件夹。 + +``` +fern/ +``` + + +将您的 gRPC 文件添加到 fern 目录中。您可以将其放在名为 `proto` 的子文件夹中,或者直接放在 fern 目录中。 + +``` +fern/ + └─ proto/ + ├─ user_service.proto + └─ common.proto +``` + + +在您的 fern 目录中添加一个 `fern.config.json` 文件,其中列出您的组织和当前版本的 Fern CLI: + +```json title="fern.config.json" +{ + "organization": "your-organization", + "version": "" +} +``` + +``` +fern/ + ├─ fern.config.json + └─ proto/ + ├─ user_service.proto + └─ common.proto +``` + + +在您的 fern 目录中创建一个 `generators.yml` 文件,并添加对您的 gRPC proto 文件的引用。请参见 [gRPC generators.yml 参考](/api-definitions/grpc/generators-yml-reference)以获取完整的配置选项。 + +```yaml title="generators.yml" +# Your API definition +api: + specs: + - proto: + # Path up to where package starts (e.g., for package userservice.v1) + root: ../user-service/proto + # Omit to generate docs for entire root folder + target: ../user-service/proto/data/v1/user_service.proto +groups: + external: + generators: + # Your C# generator configuration here, if relevant +``` + + +如果您想要[本地编译 `.proto` 文件 (`local-generation: true`)](/learn/api-definitions/grpc/generators-yml-reference#local-generation),您必须在您的机器上或[在您的 CI/CD 环境中](/learn/api-definitions/grpc/sync-your-g-rpc-specification)安装 [buf](https://buf.build/docs/installation)。 + + +您的最终目录结构: + +``` +fern/ + ├─ fern.config.json + ├─ generators.yml + └─ proto/ + ├─ user_service.proto + └─ common.proto +``` + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/reference/generators-reference.mdx b/fern/translations/zh/products/api-def/grpc/reference/generators-reference.mdx new file mode 100644 index 000000000..e8af65b59 --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/reference/generators-reference.mdx @@ -0,0 +1,11 @@ +--- +title: gRPC generators.yml 参考 +description: Fern 完整的 gRPC generators.yml 参考指南。了解如何配置 root、target、overrides 和 local-generation 设置。 +sidebar-title: gRPC generators.yml 参考 +--- + +`generators.yml` 文件有两个作用:它声明你的 gRPC 规范位置(在 `api.specs` 部分),并配置 SDK 生成(在可选的 `groups` 部分)。 + +在此声明的 API 可以通过 `docs.yml` 在你的文档中渲染。请参阅 [生成你的 API 参考](/learn/docs/api-references/overview)。 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/servers.mdx b/fern/translations/zh/products/api-def/grpc/servers.mdx new file mode 100644 index 000000000..32bca6d3b --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/servers.mdx @@ -0,0 +1,428 @@ +--- +title: 服务器 +description: 配置具有 TLS、负载均衡和部署选项的 gRPC 服务器 +subtitle: 为生产部署设置和配置 gRPC 服务器 +sidebar-title: 服务器 +--- + +gRPC 服务器可以配置各种安全性、性能和可扩展性选项。正确的服务器配置对于生产部署至关重要。 + +## 基本服务器设置 + +设置具有多个服务的基本 gRPC 服务器: + +```python title="server.py" +import grpc +from concurrent import futures +import user_service_pb2_grpc +import auth_service_pb2_grpc +from user_service import UserServiceServicer +from auth_service import AuthServiceServicer + +def create_server(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + # Add services + user_service_pb2_grpc.add_UserServiceServicer_to_server( + UserServiceServicer(), server + ) + auth_service_pb2_grpc.add_AuthServiceServicer_to_server( + AuthServiceServicer(), server + ) + + # Listen on insecure port for development + server.add_insecure_port('[::]:50051') + + return server + +if __name__ == '__main__': + server = create_server() + server.start() + print("gRPC server started on port 50051") + server.wait_for_termination() +``` + +## TLS 配置 + +为安全的生产部署配置 TLS: + +```python title="secure_server.py" +import grpc +from grpc import ssl_server_credentials + +def create_secure_server(): + # Load TLS certificates + with open('server-key.pem', 'rb') as f: + private_key = f.read() + with open('server-cert.pem', 'rb') as f: + certificate_chain = f.read() + with open('ca-cert.pem', 'rb') as f: + root_certificates = f.read() + + # Create server credentials + server_credentials = ssl_server_credentials( + [(private_key, certificate_chain)], + root_certificates=root_certificates, + require_client_auth=True # Mutual TLS + ) + + server = grpc.server(futures.ThreadPoolExecutor(max_workers=50)) + + # Add services + user_service_pb2_grpc.add_UserServiceServicer_to_server( + UserServiceServicer(), server + ) + + # Listen on secure port + server.add_secure_port('[::]:443', server_credentials) + + return server +``` + +## 服务器选项 + +配置各种服务器选项以优化性能和行为: + +```python title="configured_server.py" +import grpc +from grpc import compression + +def create_configured_server(): + # Define server options + options = [ + ('grpc.keepalive_time_ms', 30000), + ('grpc.keepalive_timeout_ms', 5000), + ('grpc.keepalive_permit_without_calls', True), + ('grpc.http2.max_pings_without_data', 0), + ('grpc.http2.min_time_between_pings_ms', 10000), + ('grpc.http2.min_ping_interval_without_data_ms', 300000), + ('grpc.max_connection_idle_ms', 60000), + ('grpc.max_connection_age_ms', 300000), + ('grpc.max_connection_age_grace_ms', 30000), + ('grpc.max_receive_message_length', 4 * 1024 * 1024), + ('grpc.max_send_message_length', 4 * 1024 * 1024), + ] + + server = grpc.server( + futures.ThreadPoolExecutor(max_workers=100), + options=options, + compression=compression.Gzip + ) + + return server +``` + +## 健康检查 + +实现用于负载均衡器集成的健康检查: + +```protobuf health.proto +syntax = "proto3"; + +package grpc.health.v1; + +service Health { + // Check health status + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); + + // Watch health status changes + rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); +} + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + SERVICE_UNKNOWN = 3; + } + ServingStatus status = 1; +} +``` + +健康服务实现: + +```python title="health_service.py" +import grpc +from grpc_health.v1 import health_pb2 +from grpc_health.v1 import health_pb2_grpc + +class HealthServicer(health_pb2_grpc.HealthServicer): + + def __init__(self): + self._service_status = {} + + def Check(self, request, context): + service_name = request.service + status = self._service_status.get( + service_name, + health_pb2.HealthCheckResponse.SERVING + ) + + return health_pb2.HealthCheckResponse(status=status) + + def Watch(self, request, context): + # Implementation for streaming health updates + service_name = request.service + + while not context.is_active(): + status = self._service_status.get( + service_name, + health_pb2.HealthCheckResponse.SERVING + ) + + yield health_pb2.HealthCheckResponse(status=status) + time.sleep(5) # Check every 5 seconds + + def set_service_status(self, service_name, status): + self._service_status[service_name] = status +``` + +## 反射 + +为开发和调试启用 gRPC 反射: + +```python title="reflection_server.py" +import grpc +from grpc_reflection.v1alpha import reflection + +def create_server_with_reflection(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + + # Add services + user_service_pb2_grpc.add_UserServiceServicer_to_server( + UserServiceServicer(), server + ) + + # Enable reflection + SERVICE_NAMES = ( + user_service_pb2.DESCRIPTOR.services_by_name['UserService'].full_name, + reflection.SERVICE_NAME, + ) + reflection.enable_server_reflection(SERVICE_NAMES, server) + + server.add_insecure_port('[::]:50051') + return server +``` + +## 负载均衡 + +配置客户端负载均衡: + +```python title="load_balanced_client.py" +import grpc + +def create_load_balanced_channel(): + # DNS-based load balancing + channel = grpc.insecure_channel( + 'dns:///user-service.example.com:50051', + options=[ + ('grpc.lb_policy_name', 'round_robin'), + ('grpc.dns_enable_srv_queries', True), + ] + ) + + return channel + +# Using a load balancer with multiple targets +def create_multi_target_channel(): + targets = [ + 'user-service-1.example.com:50051', + 'user-service-2.example.com:50051', + 'user-service-3.example.com:50051', + ] + + # Use a service mesh or load balancer + channel = grpc.insecure_channel( + f'ipv4:///{",".join(targets)}', + options=[('grpc.lb_policy_name', 'round_robin')] + ) + + return channel +``` + +## Kubernetes 部署 + +在 Kubernetes 上部署 gRPC 服务: + +```yaml title="grpc-service.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 3 + selector: + matchLabels: + app: user-service + template: + metadata: + labels: + app: user-service + spec: + containers: + - name: user-service + image: user-service:latest + ports: + - containerPort: 50051 + name: grpc + env: + - name: GRPC_PORT + value: "50051" + livenessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] + initialDelaySeconds: 30 + readinessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] + initialDelaySeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: user-service +spec: + selector: + app: user-service + ports: + - port: 50051 + targetPort: 50051 + name: grpc + type: ClusterIP +``` + +## 监控和可观测性 + +为你的 gRPC 服务器添加监控和跟踪: + +```python title="monitored_server.py" +import grpc +import time +from prometheus_client import Counter, Histogram, start_http_server + +# Prometheus metrics +REQUEST_COUNT = Counter( + 'grpc_requests_total', + 'Total gRPC requests', + ['method', 'status'] +) + +REQUEST_DURATION = Histogram( + 'grpc_request_duration_seconds', + 'gRPC request duration', + ['method'] +) + +class MonitoringInterceptor(grpc.ServerInterceptor): + + def intercept_service(self, continuation, handler_call_details): + method = handler_call_details.method + start_time = time.time() + + def monitor_wrapper(behavior): + def wrapper(request, context): + try: + response = behavior(request, context) + REQUEST_COUNT.labels(method=method, status='OK').inc() + return response + except Exception as e: + REQUEST_COUNT.labels(method=method, status='ERROR').inc() + raise + finally: + duration = time.time() - start_time + REQUEST_DURATION.labels(method=method).observe(duration) + + return wrapper + + return grpc.unary_unary_rpc_method_handler( + monitor_wrapper(continuation(handler_call_details).unary_unary) + ) + +def create_monitored_server(): + # Start Prometheus metrics server + start_http_server(8000) + + server = grpc.server( + futures.ThreadPoolExecutor(max_workers=10), + interceptors=[MonitoringInterceptor()] + ) + + return server +``` + +## 环境特定配置 + +为不同环境配置服务器: + +```python title="config.py" +import os +from dataclasses import dataclass + +@dataclass +class ServerConfig: + port: int + max_workers: int + enable_tls: bool + cert_file: str = None + key_file: str = None + enable_reflection: bool = False + enable_health_check: bool = True + +def get_config() -> ServerConfig: + env = os.getenv('ENVIRONMENT', 'development') + + if env == 'production': + return ServerConfig( + port=50051, + max_workers=100, + enable_tls=True, + cert_file='/etc/ssl/certs/server.crt', + key_file='/etc/ssl/private/server.key', + enable_reflection=False, + enable_health_check=True + ) + elif env == 'staging': + return ServerConfig( + port=50051, + max_workers=50, + enable_tls=True, + cert_file='/etc/ssl/certs/staging.crt', + key_file='/etc/ssl/private/staging.key', + enable_reflection=True, + enable_health_check=True + ) + else: # development + return ServerConfig( + port=50051, + max_workers=10, + enable_tls=False, + enable_reflection=True, + enable_health_check=True + ) + +def create_server_from_config(config: ServerConfig): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=config.max_workers)) + + # Add services... + + if config.enable_tls: + # Configure TLS + with open(config.cert_file, 'rb') as f: + cert = f.read() + with open(config.key_file, 'rb') as f: + key = f.read() + + credentials = grpc.ssl_server_credentials([(key, cert)]) + server.add_secure_port(f'[::]:{config.port}', credentials) + else: + server.add_insecure_port(f'[::]:{config.port}') + + return server +``` + +正确的服务器配置确保你的 gRPC 服务安全、高性能并为生产工作负载做好准备。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/services/errors.mdx b/fern/translations/zh/products/api-def/grpc/services/errors.mdx new file mode 100644 index 000000000..7ea271b06 --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/services/errors.mdx @@ -0,0 +1,528 @@ +--- +title: 错误处理 +subtitle: 使用 gRPC 状态码和自定义错误详情实现强大的错误处理 +sidebar-title: 错误处理 +--- + + +gRPC 提供了一个全面的错误处理系统,使用状态码、错误消息和可选的错误详情。正确的错误处理确保客户端能够适当地响应不同的故障场景。 + +## gRPC 状态码 + +gRPC 使用标准状态码来指示 RPC 调用的结果: + +```protobuf title="error_service.proto" +syntax = "proto3"; + +package errors.v1; + +import "google/rpc/status.proto"; +import "google/rpc/error_details.proto"; + +service ErrorDemoService { + // 演示各种错误场景 + rpc ValidateUser(ValidateUserRequest) returns (ValidateUserResponse); + + // 可能返回速率限制错误 + rpc RateLimitedOperation(OperationRequest) returns (OperationResponse); + + // 可能返回资源耗尽错误 + rpc ResourceIntensiveOperation(ResourceRequest) returns (ResourceResponse); +} + +message ValidateUserRequest { + string user_id = 1; + string email = 2; +} + +message ValidateUserResponse { + bool valid = 1; + repeated ValidationError errors = 2; +} + +message ValidationError { + string field = 1; + string message = 2; + ErrorCode code = 3; +} + +enum ErrorCode { + ERROR_CODE_UNSPECIFIED = 0; + ERROR_CODE_REQUIRED_FIELD = 1; + ERROR_CODE_INVALID_FORMAT = 2; + ERROR_CODE_DUPLICATE_VALUE = 3; + ERROR_CODE_OUT_OF_RANGE = 4; +} +``` + +## 标准错误处理 + +实现标准 gRPC 错误响应: + +```python title="error_handling.py" +import grpc +from grpc import ServicerContext +from google.rpc import status_pb2, error_details_pb2 +from google.protobuf import any_pb2 + +class ErrorDemoServiceServicer(errors_pb2_grpc.ErrorDemoServiceServicer): + + def ValidateUser( + self, + request: errors_pb2.ValidateUserRequest, + context: ServicerContext + ) -> errors_pb2.ValidateUserResponse: + """演示验证错误。""" + + validation_errors = [] + + # 检查必填字段 + if not request.user_id: + validation_errors.append(errors_pb2.ValidationError( + field="user_id", + message="User ID is required", + code=errors_pb2.ERROR_CODE_REQUIRED_FIELD + )) + + if not request.email: + validation_errors.append(errors_pb2.ValidationError( + field="email", + message="Email is required", + code=errors_pb2.ERROR_CODE_REQUIRED_FIELD + )) + + # 验证邮箱格式 + if request.email and not self._is_valid_email(request.email): + validation_errors.append(errors_pb2.ValidationError( + field="email", + message="Invalid email format", + code=errors_pb2.ERROR_CODE_INVALID_FORMAT + )) + + # 检查用户是否重复 + if request.user_id and self.user_exists(request.user_id): + validation_errors.append(errors_pb2.ValidationError( + field="user_id", + message="User ID already exists", + code=errors_pb2.ERROR_CODE_DUPLICATE_VALUE + )) + + # 如果有验证错误则返回 + if validation_errors: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details("Validation failed") + + return errors_pb2.ValidateUserResponse( + valid=False, + errors=validation_errors + ) + + return errors_pb2.ValidateUserResponse(valid=True) + + def RateLimitedOperation( + self, + request: errors_pb2.OperationRequest, + context: ServicerContext + ) -> errors_pb2.OperationResponse: + """演示速率限制错误。""" + + # 检查速率限制 + if not self.rate_limiter.is_allowed(request.client_id): + # 创建详细的错误信息 + retry_info = error_details_pb2.RetryInfo() + retry_info.retry_delay.seconds = 60 # 60 秒后重试 + + quota_failure = error_details_pb2.QuotaFailure() + violation = quota_failure.violations.add() + violation.subject = f"client:{request.client_id}" + violation.description = "API rate limit exceeded" + + # 设置带详情的错误 + context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED) + context.set_details("Rate limit exceeded") + + # 添加错误详情(丰富的错误信息) + self._add_error_details(context, [retry_info, quota_failure]) + + return errors_pb2.OperationResponse() + + # 正常处理操作 + return self._process_operation(request) + + def ResourceIntensiveOperation( + self, + request: errors_pb2.ResourceRequest, + context: ServicerContext + ) -> errors_pb2.ResourceResponse: + """演示资源耗尽错误。""" + + try: + # 检查系统资源 + if not self.resource_manager.has_capacity(): + context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED) + context.set_details("System overloaded, please try again later") + return errors_pb2.ResourceResponse() + + # 处理密集型操作 + result = self._process_intensive_operation(request) + return errors_pb2.ResourceResponse(result=result) + + except TimeoutError: + context.set_code(grpc.StatusCode.DEADLINE_EXCEEDED) + context.set_details("Operation timed out") + return errors_pb2.ResourceResponse() + + except PermissionError: + context.set_code(grpc.StatusCode.PERMISSION_DENIED) + context.set_details("Insufficient permissions for this operation") + return errors_pb2.ResourceResponse() + + except FileNotFoundError as e: + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(f"Required resource not found: {str(e)}") + return errors_pb2.ResourceResponse() + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f"Internal server error: {str(e)}") + return errors_pb2.ResourceResponse() + + def _add_error_details(self, context: ServicerContext, details): + """向 gRPC 响应添加丰富的错误详情。""" + rich_status = status_pb2.Status() + rich_status.code = context._state.code.value[0] + rich_status.message = context._state.details + + for detail in details: + detail_any = any_pb2.Any() + detail_any.Pack(detail) + rich_status.details.append(detail_any) + + # 添加到尾随元数据 + context.set_trailing_metadata([ + ('grpc-status-details-bin', rich_status.SerializeToString()) + ]) +``` + +## 自定义错误类型 + +为特定领域的错误定义自定义错误类型: + +```protobuf title="custom_errors.proto" +syntax = "proto3"; + +package errors.v1; + +// 自定义错误详情 +message BusinessLogicError { + string error_code = 1; + string error_message = 2; + map error_context = 3; + repeated string suggested_actions = 4; +} + +message ValidationFailure { + repeated FieldError field_errors = 1; + string global_error = 2; +} + +message FieldError { + string field_path = 1; + string error_message = 2; + string error_code = 3; + google.protobuf.Any invalid_value = 4; +} + +message ServiceUnavailableError { + string service_name = 1; + google.protobuf.Timestamp estimated_recovery_time = 2; + repeated string alternative_endpoints = 3; +} +``` + +自定义错误实现: + +```python title="custom_errors.py" +class CustomErrorHandler: + + @staticmethod + def business_logic_error( + context: ServicerContext, + error_code: str, + message: str, + error_context: dict = None, + suggested_actions: list = None + ): + """创建业务逻辑错误。""" + + business_error = errors_pb2.BusinessLogicError( + error_code=error_code, + error_message=message, + error_context=error_context or {}, + suggested_actions=suggested_actions or [] + ) + + context.set_code(grpc.StatusCode.FAILED_PRECONDITION) + context.set_details(message) + + # 添加自定义错误详情 + CustomErrorHandler._add_custom_details(context, business_error) + + @staticmethod + def validation_failure( + context: ServicerContext, + field_errors: list, + global_error: str = None + ): + """创建验证失败错误。""" + + validation_error = errors_pb2.ValidationFailure( + field_errors=field_errors, + global_error=global_error or "" + ) + + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details("Validation failed") + + CustomErrorHandler._add_custom_details(context, validation_error) + + @staticmethod + def service_unavailable( + context: ServicerContext, + service_name: str, + estimated_recovery: datetime = None, + alternatives: list = None + ): + """创建服务不可用错误。""" + + error = errors_pb2.ServiceUnavailableError( + service_name=service_name, + alternative_endpoints=alternatives or [] + ) + + if estimated_recovery: + error.estimated_recovery_time.FromDatetime(estimated_recovery) + + context.set_code(grpc.StatusCode.UNAVAILABLE) + context.set_details(f"Service {service_name} is currently unavailable") + + CustomErrorHandler._add_custom_details(context, error) + + @staticmethod + def _add_custom_details(context: ServicerContext, error_detail): + """向响应添加自定义错误详情。""" + detail_any = any_pb2.Any() + detail_any.Pack(error_detail) + + rich_status = status_pb2.Status() + rich_status.code = context._state.code.value[0] + rich_status.message = context._state.details + rich_status.details.append(detail_any) + + context.set_trailing_metadata([ + ('grpc-status-details-bin', rich_status.SerializeToString()) + ]) + +# 使用示例 +class UserService(user_pb2_grpc.UserServiceServicer): + + def CreateUser(self, request, context): + # 验证业务规则 + if self.user_repository.email_exists(request.email): + CustomErrorHandler.business_logic_error( + context, + error_code="DUPLICATE_EMAIL", + message="Email address is already registered", + error_context={"email": request.email}, + suggested_actions=[ + "Use a different email address", + "Reset password if you forgot your account" + ] + ) + return user_pb2.User() + + # 继续用户创建... +``` + +## 错误拦截器 + +使用拦截器实现全局错误处理: + +```python title="error_interceptor.py" +import logging +import traceback +from grpc import ServicerContext, StatusCode + +class ErrorInterceptor(grpc.ServerInterceptor): + + def __init__(self, logger=None): + self.logger = logger or logging.getLogger(__name__) + + def intercept_service(self, continuation, handler_call_details): + def error_wrapper(behavior): + def wrapper(request, context): + try: + return behavior(request, context) + + except ValueError as e: + # 将 Python ValueError 转换为 gRPC INVALID_ARGUMENT + context.set_code(StatusCode.INVALID_ARGUMENT) + context.set_details(f"Invalid input: {str(e)}") + self.logger.warning(f"Validation error in {handler_call_details.method}: {e}") + return self._get_default_response(behavior) + + except PermissionError as e: + context.set_code(StatusCode.PERMISSION_DENIED) + context.set_details("Access denied") + self.logger.warning(f"Permission denied in {handler_call_details.method}: {e}") + return self._get_default_response(behavior) + + except TimeoutError as e: + context.set_code(StatusCode.DEADLINE_EXCEEDED) + context.set_details("Operation timed out") + self.logger.error(f"Timeout in {handler_call_details.method}: {e}") + return self._get_default_response(behavior) + + except Exception as e: + # 记录未预期的错误 + self.logger.error( + f"Unexpected error in {handler_call_details.method}: {e}\n" + f"Traceback: {traceback.format_exc()}" + ) + + context.set_code(StatusCode.INTERNAL) + context.set_details("Internal server error") + return self._get_default_response(behavior) + + return wrapper + + return grpc.unary_unary_rpc_method_handler( + error_wrapper(continuation(handler_call_details).unary_unary) + ) + + def _get_default_response(self, behavior): + """返回正确类型的空响应。""" + # 这需要根据您的服务方法进行实现 + return None +``` + +## 客户端错误处理 + +在客户端处理错误: + +```python title="client_error_handling.py" +import grpc +from google.rpc import status_pb2, error_details_pb2 +from google.protobuf import any_pb2 + +def handle_grpc_errors(stub_method, request): + """gRPC 客户端调用的通用错误处理。""" + + try: + response = stub_method(request) + return response, None + + except grpc.RpcError as e: + error_info = { + 'code': e.code(), + 'details': e.details(), + 'status': e.code().name + } + + # 如果可用,提取丰富的错误详情 + metadata = dict(e.trailing_metadata()) + if 'grpc-status-details-bin' in metadata: + try: + status_detail = status_pb2.Status() + status_detail.ParseFromString(metadata['grpc-status-details-bin']) + + error_info['rich_details'] = [] + for detail in status_detail.details: + # 尝试解包常见错误类型 + if detail.Is(error_details_pb2.RetryInfo.DESCRIPTOR): + retry_info = error_details_pb2.RetryInfo() + detail.Unpack(retry_info) + error_info['rich_details'].append({ + 'type': 'retry_info', + 'retry_delay_seconds': retry_info.retry_delay.seconds + }) + elif detail.Is(error_details_pb2.QuotaFailure.DESCRIPTOR): + quota_failure = error_details_pb2.QuotaFailure() + detail.Unpack(quota_failure) + error_info['rich_details'].append({ + 'type': 'quota_failure', + 'violations': [ + { + 'subject': v.subject, + 'description': v.description + } for v in quota_failure.violations + ] + }) + + except Exception: + # 如果无法解析丰富的详情,那也没关系 + pass + + return None, error_info + +# 使用示例 +def create_user_with_error_handling(stub, user_data): + """创建用户并进行全面的错误处理。""" + + request = user_pb2.CreateUserRequest(**user_data) + response, error = handle_grpc_errors(stub.CreateUser, request) + + if error: + if error['code'] == grpc.StatusCode.INVALID_ARGUMENT: + print(f"Validation failed: {error['details']}") + return None + + elif error['code'] == grpc.StatusCode.ALREADY_EXISTS: + print(f"User already exists: {error['details']}") + return None + + elif error['code'] == grpc.StatusCode.RESOURCE_EXHAUSTED: + # 检查重试信息 + for detail in error.get('rich_details', []): + if detail['type'] == 'retry_info': + retry_delay = detail['retry_delay_seconds'] + print(f"Rate limited. Retry after {retry_delay} seconds") + return None + + print("Resource exhausted") + return None + + else: + print(f"Unexpected error: {error['status']} - {error['details']}") + return None + + return response +``` + +## 错误响应模式 + +定义一致的错误响应模式: + +```protobuf title="error_responses.proto" +syntax = "proto3"; + +// 标准错误响应包装器 +message ErrorResponse { + string error_code = 1; + string error_message = 2; + map error_context = 3; + repeated string suggestions = 4; + google.protobuf.Timestamp timestamp = 5; + string request_id = 6; +} + +// 联合响应模式 +message CreateUserResult { + oneof result { + User success = 1; + ErrorResponse error = 2; + } +} +``` + +gRPC 中正确的错误处理确保了强大、可维护的服务,为客户端提供关于出了什么问题以及如何修复的明确反馈。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/services/grpc-services.mdx b/fern/translations/zh/products/api-def/grpc/services/grpc-services.mdx new file mode 100644 index 000000000..12336eb24 --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/services/grpc-services.mdx @@ -0,0 +1,449 @@ +--- +title: gRPC 服务 +subtitle: 使用 RPC、消息和 Protocol Buffer 模式定义 gRPC 服务 +sidebar-title: gRPC 服务 +--- + +gRPC 服务是 API 的核心构建块。每个服务定义了客户端可以调用的远程过程调用(RPC)集合,以及用于请求和响应的消息类型。 + +## 服务定义 + +在 `.proto` 文件中定义 gRPC 服务: + +```protobuf title="user_service.proto" +syntax = "proto3"; + +package userservice.v1; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/field_mask.proto"; + +// User management service +service UserService { + // Create a new user account + rpc CreateUser(CreateUserRequest) returns (User) { + option deprecated = false; + } + + // Get user by ID + rpc GetUser(GetUserRequest) returns (User); + + // Update user information + rpc UpdateUser(UpdateUserRequest) returns (User); + + // Delete a user account + rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty); + + // List users with pagination + rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); + + // Search users by various criteria + rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse); +} + +// User message definition +message User { + string id = 1; + string email = 2; + string name = 3; + int32 age = 4; + UserStatus status = 5; + google.protobuf.Timestamp created_at = 6; + google.protobuf.Timestamp updated_at = 7; + repeated string roles = 8; + UserPreferences preferences = 9; +} + +// User status enumeration +enum UserStatus { + USER_STATUS_UNSPECIFIED = 0; + USER_STATUS_ACTIVE = 1; + USER_STATUS_INACTIVE = 2; + USER_STATUS_SUSPENDED = 3; + USER_STATUS_DELETED = 4; +} + +// Nested message for user preferences +message UserPreferences { + bool email_notifications = 1; + string timezone = 2; + string language = 3; + ThemeMode theme = 4; +} + +enum ThemeMode { + THEME_MODE_UNSPECIFIED = 0; + THEME_MODE_LIGHT = 1; + THEME_MODE_DARK = 2; + THEME_MODE_AUTO = 3; +} +``` + +## 请求和响应消息 + +定义清晰的请求和响应消息类型: + +```protobuf title="user_messages.proto" +// Create user request +message CreateUserRequest { + string email = 1 [(validate.rules).string.email = true]; + string name = 2 [(validate.rules).string.min_len = 1]; + int32 age = 3 [(validate.rules).int32.gte = 0]; + UserPreferences preferences = 4; +} + +// Get user request +message GetUserRequest { + string id = 1 [(validate.rules).string.uuid = true]; +} + +// Update user request +message UpdateUserRequest { + string id = 1 [(validate.rules).string.uuid = true]; + User user = 2; + google.protobuf.FieldMask update_mask = 3; +} + +// Delete user request +message DeleteUserRequest { + string id = 1 [(validate.rules).string.uuid = true]; +} + +// List users request with pagination +message ListUsersRequest { + int32 page_size = 1 [(validate.rules).int32 = {gte: 1, lte: 100}]; + string page_token = 2; + string filter = 3; + string order_by = 4; +} + +// List users response +message ListUsersResponse { + repeated User users = 1; + string next_page_token = 2; + int32 total_count = 3; +} + +// Search users request +message SearchUsersRequest { + string query = 1 [(validate.rules).string.min_len = 1]; + repeated UserStatus status_filter = 2; + repeated string role_filter = 3; + int32 page_size = 4 [(validate.rules).int32 = {gte: 1, lte: 100}]; + string page_token = 5; +} + +// Search users response +message SearchUsersResponse { + repeated User users = 1; + string next_page_token = 2; + int32 total_count = 3; + SearchMetadata metadata = 4; +} + +message SearchMetadata { + int32 search_time_ms = 1; + repeated string suggested_corrections = 2; +} +``` + +## 服务实现 + +使用您首选的语言实现服务: + +```python title="user_service.py" +import grpc +from grpc import ServicerContext +import user_service_pb2 +import user_service_pb2_grpc +from google.protobuf import empty_pb2 +from typing import Iterator + +class UserServiceServicer(user_service_pb2_grpc.UserServiceServicer): + + def __init__(self, user_repository): + self.user_repository = user_repository + + def CreateUser( + self, + request: user_service_pb2.CreateUserRequest, + context: ServicerContext + ) -> user_service_pb2.User: + """Create a new user account.""" + try: + # Validate request + if not request.email or not request.name: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('Email and name are required') + return user_service_pb2.User() + + # Check if user already exists + if self.user_repository.get_by_email(request.email): + context.set_code(grpc.StatusCode.ALREADY_EXISTS) + context.set_details(f'User with email {request.email} already exists') + return user_service_pb2.User() + + # Create user + user = self.user_repository.create_user( + email=request.email, + name=request.name, + age=request.age, + preferences=request.preferences + ) + + return user + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to create user: {str(e)}') + return user_service_pb2.User() + + def GetUser( + self, + request: user_service_pb2.GetUserRequest, + context: ServicerContext + ) -> user_service_pb2.User: + """Get user by ID.""" + try: + user = self.user_repository.get_by_id(request.id) + if not user: + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(f'User with ID {request.id} not found') + return user_service_pb2.User() + + return user + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to get user: {str(e)}') + return user_service_pb2.User() + + def UpdateUser( + self, + request: user_service_pb2.UpdateUserRequest, + context: ServicerContext + ) -> user_service_pb2.User: + """Update user information.""" + try: + # Check if user exists + existing_user = self.user_repository.get_by_id(request.id) + if not existing_user: + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(f'User with ID {request.id} not found') + return user_service_pb2.User() + + # Apply field mask for partial updates + updated_user = self.user_repository.update_user( + user_id=request.id, + updates=request.user, + field_mask=request.update_mask + ) + + return updated_user + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to update user: {str(e)}') + return user_service_pb2.User() + + def DeleteUser( + self, + request: user_service_pb2.DeleteUserRequest, + context: ServicerContext + ) -> empty_pb2.Empty: + """Delete a user account.""" + try: + # Check if user exists + user = self.user_repository.get_by_id(request.id) + if not user: + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(f'User with ID {request.id} not found') + return empty_pb2.Empty() + + # Soft delete user + self.user_repository.delete_user(request.id) + + return empty_pb2.Empty() + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to delete user: {str(e)}') + return empty_pb2.Empty() + + def ListUsers( + self, + request: user_service_pb2.ListUsersRequest, + context: ServicerContext + ) -> user_service_pb2.ListUsersResponse: + """List users with pagination.""" + try: + # Apply pagination + page_size = min(request.page_size or 20, 100) + + users, next_page_token, total_count = self.user_repository.list_users( + page_size=page_size, + page_token=request.page_token, + filter_expr=request.filter, + order_by=request.order_by + ) + + return user_service_pb2.ListUsersResponse( + users=users, + next_page_token=next_page_token, + total_count=total_count + ) + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to list users: {str(e)}') + return user_service_pb2.ListUsersResponse() + + def SearchUsers( + self, + request: user_service_pb2.SearchUsersRequest, + context: ServicerContext + ) -> user_service_pb2.SearchUsersResponse: + """Search users by various criteria.""" + try: + start_time = time.time() + + users, next_page_token, total_count = self.user_repository.search_users( + query=request.query, + status_filter=request.status_filter, + role_filter=request.role_filter, + page_size=request.page_size or 20, + page_token=request.page_token + ) + + search_time_ms = int((time.time() - start_time) * 1000) + + metadata = user_service_pb2.SearchMetadata( + search_time_ms=search_time_ms, + suggested_corrections=[] # Add spell check suggestions if needed + ) + + return user_service_pb2.SearchUsersResponse( + users=users, + next_page_token=next_page_token, + total_count=total_count, + metadata=metadata + ) + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Failed to search users: {str(e)}') + return user_service_pb2.SearchUsersResponse() +``` + +## Protocol Buffer 最佳实践 + +### 字段编号 +- 对于频繁使用的字段使用字段编号 1-15(编码更高效) +- 为已删除的字段保留字段编号以维持兼容性 +- 永远不要重复使用字段编号 + +```protobuf +message User { + // 频繁使用的字段 (1-15) + string id = 1; + string email = 2; + string name = 3; + + // 不太频繁使用的字段 + UserPreferences preferences = 16; + repeated string tags = 17; + + // 保留字段 + reserved 4, 5, 6; + reserved "old_field_name", "deprecated_field"; +} +``` + +### 命名规范 +- 字段名使用 `snake_case` +- 消息和服务名使用 `PascalCase` +- 枚举值使用 `UPPER_SNAKE_CASE` + +```protobuf +service UserManagementService { // PascalCase + rpc GetUser(GetUserRequest) returns (User); +} + +message User { // PascalCase + string first_name = 1; // snake_case + UserStatus status = 2; +} + +enum UserStatus { + USER_STATUS_UNSPECIFIED = 0; // UPPER_SNAKE_CASE + USER_STATUS_ACTIVE = 1; +} +``` + +### 版本管理 +- 在包名中包含版本 +- 对于破坏性更改使用语义版本控制 + +```protobuf +syntax = "proto3"; + +package userservice.v1; // 包名中的版本 + +option go_package = "example.com/userservice/v1"; +``` + +## 多个服务 + +将相关功能组织到单独的服务中: + +```protobuf title="services.proto" +// 用户管理 +service UserService { + rpc CreateUser(CreateUserRequest) returns (User); + rpc GetUser(GetUserRequest) returns (User); +} + +// 身份验证 +service AuthService { + rpc Login(LoginRequest) returns (LoginResponse); + rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse); +} + +// 通知服务 +service NotificationService { + rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse); + rpc GetNotificationPreferences(GetNotificationPreferencesRequest) returns (NotificationPreferences); +} +``` + +gRPC 服务为构建分布式系统提供了强类型、高性能的基础,在客户端和服务器之间建立了清晰的合约。 + +## 自定义选项 + +Fern 提供自定义 Protocol Buffer 选项来增强您的 API 参考文档和生成的代码。 + +### API 导航名称 + +使用 `fern.summary` 选项为端点设置明确的显示名称,比默认的 RPC 方法名称更用户友好。 + + +```protobuf +// Import Fern custom options +import "fern/options.proto"; + +service CommentsService { + // Description of the endpoint + rpc CreateComment(CreateCommentRequest) returns (CreateCommentResponse) { + option (google.api.http) = { + post: "/comments/v1/comments" + response_body: "comment" + body: "*" + }; + + // Display name shown in navigation + option (fern.summary) = "Create your comment"; + } +} +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/grpc/services/streaming.mdx b/fern/translations/zh/products/api-def/grpc/services/streaming.mdx new file mode 100644 index 000000000..643f56a35 --- /dev/null +++ b/fern/translations/zh/products/api-def/grpc/services/streaming.mdx @@ -0,0 +1,516 @@ +--- +title: 流式传输 +description: 学习如何在 gRPC 中实现服务器流式传输、客户端流式传输和双向流式传输,以实现实时通信和高效的数据传输。 +sidebar-title: 流式传输 +--- + + +gRPC 支持四种类型的服务方法:一元调用、服务器流式传输、客户端流式传输和双向流式传输。流式传输可实现高效的实时通信和大数据传输。 + +## 服务器流式传输 + +服务器流式传输允许服务器对单个客户端请求发送多个响应: + +```protobuf title="streaming_service.proto" +syntax = "proto3"; + +package streaming.v1; + +service StreamingService { + // 服务器流式传输:向客户端发送多个事件 + rpc StreamUserEvents(StreamUserEventsRequest) returns (stream UserEvent); + + // 服务器流式传输:分块下载大文件 + rpc DownloadFile(DownloadFileRequest) returns (stream FileChunk); + + // 服务器流式传输:实时通知 + rpc SubscribeToNotifications(SubscribeRequest) returns (stream Notification); +} + +message StreamUserEventsRequest { + string user_id = 1; + repeated UserEventType event_types = 2; + google.protobuf.Timestamp start_time = 3; +} + +message UserEvent { + string id = 1; + string user_id = 2; + UserEventType type = 3; + google.protobuf.Timestamp timestamp = 4; + google.protobuf.Any data = 5; +} + +enum UserEventType { + USER_EVENT_TYPE_UNSPECIFIED = 0; + USER_EVENT_TYPE_LOGIN = 1; + USER_EVENT_TYPE_LOGOUT = 2; + USER_EVENT_TYPE_PROFILE_UPDATE = 3; + USER_EVENT_TYPE_PASSWORD_CHANGE = 4; +} +``` + +服务器流式传输实现: + +```python title="server_streaming.py" +import grpc +import time +import asyncio +from grpc import ServicerContext +from typing import Iterator + +class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer): + + def StreamUserEvents( + self, + request: streaming_pb2.StreamUserEventsRequest, + context: ServicerContext + ) -> Iterator[streaming_pb2.UserEvent]: + """实时流式传输用户事件。""" + + # 验证请求 + if not request.user_id: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('User ID is required') + return + + # 订阅事件流 + event_subscriber = self.event_store.subscribe( + user_id=request.user_id, + event_types=request.event_types, + start_time=request.start_time + ) + + try: + while context.is_active(): + # 等待下一个事件(带超时) + try: + event = event_subscriber.get_next_event(timeout=30) + if event: + yield event + else: + # 发送心跳以保持连接活跃 + continue + + except TimeoutError: + # 检查客户端是否仍然连接 + if not context.is_active(): + break + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Error streaming events: {str(e)}') + break + + finally: + # 清理订阅 + event_subscriber.close() + + def DownloadFile( + self, + request: streaming_pb2.DownloadFileRequest, + context: ServicerContext + ) -> Iterator[streaming_pb2.FileChunk]: + """分块下载文件。""" + + try: + file_path = self.file_store.get_file_path(request.file_id) + if not file_path or not os.path.exists(file_path): + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(f'File {request.file_id} not found') + return + + chunk_size = 64 * 1024 # 64KB 块大小 + + with open(file_path, 'rb') as f: + while True: + chunk_data = f.read(chunk_size) + if not chunk_data: + break + + # 检查客户端是否断开连接 + if not context.is_active(): + break + + yield streaming_pb2.FileChunk( + data=chunk_data, + offset=f.tell() - len(chunk_data), + size=len(chunk_data) + ) + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Error downloading file: {str(e)}') +``` + +## 客户端流式传输 + +客户端流式传输允许客户端发送多个请求并接收单个响应: + +```protobuf title="client_streaming.proto" +service StreamingService { + // 客户端流式传输:分块上传大文件 + rpc UploadFile(stream FileChunk) returns (UploadFileResponse); + + // 客户端流式传输:批量数据处理 + rpc ProcessDataBatch(stream DataRecord) returns (BatchProcessingResult); + + // 客户端流式传输:实时指标收集 + rpc CollectMetrics(stream MetricData) returns (MetricsCollectionResult); +} + +message FileChunk { + oneof data { + FileMetadata metadata = 1; + bytes chunk = 2; + } +} + +message FileMetadata { + string filename = 1; + int64 total_size = 2; + string content_type = 3; + string checksum = 4; +} + +message UploadFileResponse { + string file_id = 1; + int64 bytes_uploaded = 2; + string download_url = 3; + bool checksum_verified = 4; +} +``` + +客户端流式传输实现: + +```python title="client_streaming.py" +class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer): + + def UploadFile( + self, + request_iterator: Iterator[streaming_pb2.FileChunk], + context: ServicerContext + ) -> streaming_pb2.UploadFileResponse: + """从客户端流上传文件。""" + + file_metadata = None + total_bytes = 0 + file_path = None + hasher = hashlib.sha256() + + try: + for chunk in request_iterator: + if chunk.HasField('metadata'): + # 第一个块包含元数据 + file_metadata = chunk.metadata + + # 创建临时文件 + file_id = str(uuid.uuid4()) + file_path = f'/tmp/uploads/{file_id}' + os.makedirs(os.path.dirname(file_path), exist_ok=True) + + elif chunk.HasField('chunk'): + # 后续块包含文件数据 + if not file_metadata: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('File metadata must be sent first') + return streaming_pb2.UploadFileResponse() + + # 将块写入文件 + with open(file_path, 'ab') as f: + f.write(chunk.chunk) + + total_bytes += len(chunk.chunk) + hasher.update(chunk.chunk) + + # 检查大小限制 + if total_bytes > file_metadata.total_size: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details('File size exceeds declared size') + return streaming_pb2.UploadFileResponse() + + # 验证校验和 + computed_checksum = hasher.hexdigest() + checksum_verified = computed_checksum == file_metadata.checksum + + if not checksum_verified: + context.set_code(grpc.StatusCode.DATA_LOSS) + context.set_details('File checksum verification failed') + return streaming_pb2.UploadFileResponse() + + # 将文件移动到永久存储 + permanent_path = self.file_store.store_file(file_id, file_path) + download_url = self.file_store.get_download_url(file_id) + + return streaming_pb2.UploadFileResponse( + file_id=file_id, + bytes_uploaded=total_bytes, + download_url=download_url, + checksum_verified=True + ) + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Error uploading file: {str(e)}') + return streaming_pb2.UploadFileResponse() + + finally: + # 清理临时文件 + if file_path and os.path.exists(file_path): + os.remove(file_path) +``` + +## 双向流式传输 + +双向流式传输允许客户端和服务器都发送多个消息: + +```protobuf title="bidirectional_streaming.proto" +service StreamingService { + // 双向流式传输:实时聊天 + rpc Chat(stream ChatMessage) returns (stream ChatMessage); + + // 双向流式传输:实时协作 + rpc Collaborate(stream CollaborationEvent) returns (stream CollaborationEvent); + + // 双向流式传输:实时数据处理 + rpc ProcessLiveData(stream DataInput) returns (stream ProcessingResult); +} + +message ChatMessage { + string id = 1; + string user_id = 2; + string room_id = 3; + string content = 4; + google.protobuf.Timestamp timestamp = 5; + ChatMessageType type = 6; +} + +enum ChatMessageType { + CHAT_MESSAGE_TYPE_UNSPECIFIED = 0; + CHAT_MESSAGE_TYPE_TEXT = 1; + CHAT_MESSAGE_TYPE_IMAGE = 2; + CHAT_MESSAGE_TYPE_FILE = 3; + CHAT_MESSAGE_TYPE_SYSTEM = 4; + CHAT_MESSAGE_TYPE_TYPING = 5; +} +``` + +双向流式传输实现: + +```python title="bidirectional_streaming.py" +import asyncio +import queue +import threading + +class StreamingServiceServicer(streaming_pb2_grpc.StreamingServiceServicer): + + def Chat( + self, + request_iterator: Iterator[streaming_pb2.ChatMessage], + context: ServicerContext + ) -> Iterator[streaming_pb2.ChatMessage]: + """双向聊天流式传输。""" + + # 出站消息队列 + outgoing_queue = queue.Queue() + + # 跟踪用户会话 + user_session = None + + def handle_incoming_messages(): + """处理来自客户端的入站消息。""" + nonlocal user_session + + try: + for message in request_iterator: + if not user_session: + # 第一条消息建立会话 + user_session = self.chat_service.join_room( + user_id=message.user_id, + room_id=message.room_id + ) + + # 发送欢迎消息 + welcome_msg = streaming_pb2.ChatMessage( + id=str(uuid.uuid4()), + user_id="system", + room_id=message.room_id, + content=f"User {message.user_id} joined the chat", + timestamp=google.protobuf.timestamp_pb2.Timestamp(), + type=streaming_pb2.CHAT_MESSAGE_TYPE_SYSTEM + ) + outgoing_queue.put(welcome_msg) + + # 处理消息 + if message.type == streaming_pb2.CHAT_MESSAGE_TYPE_TEXT: + # 广播给房间内其他用户 + self.chat_service.broadcast_message(message) + + elif message.type == streaming_pb2.CHAT_MESSAGE_TYPE_TYPING: + # 向其他用户发送打字指示器 + self.chat_service.broadcast_typing(message) + + except Exception as e: + print(f"Error handling incoming messages: {e}") + finally: + # 清理会话 + if user_session: + self.chat_service.leave_room(user_session) + + # 启动处理入站消息的后台线程 + incoming_thread = threading.Thread(target=handle_incoming_messages) + incoming_thread.daemon = True + incoming_thread.start() + + # 订阅房间消息 + message_subscriber = None + if user_session: + message_subscriber = self.chat_service.subscribe_to_room( + user_session.room_id, + exclude_user=user_session.user_id + ) + + try: + while context.is_active(): + # 检查队列中的出站消息 + try: + message = outgoing_queue.get(timeout=1) + yield message + except queue.Empty: + pass + + # 检查来自其他用户的消息 + if message_subscriber: + try: + room_message = message_subscriber.get_message(timeout=1) + if room_message: + yield room_message + except TimeoutError: + pass + + finally: + # 清理 + if message_subscriber: + message_subscriber.close() + if user_session: + self.chat_service.leave_room(user_session) +``` + +## 流式传输最佳实践 + +### 流量控制 +实现适当的流量控制以防止客户端不堪重负: + +```python title="flow_control.py" +def StreamData(self, request, context): + """带流量控制的流式传输。""" + + # 使用有界队列控制内存使用 + data_queue = queue.Queue(maxsize=100) + + def data_producer(): + """生成数据的后台线程。""" + for item in self.data_source.get_items(): + try: + data_queue.put(item, timeout=5) + except queue.Full: + # 应用反压 + print("Client is too slow, dropping data") + break + + producer_thread = threading.Thread(target=data_producer) + producer_thread.start() + + try: + while context.is_active(): + try: + item = data_queue.get(timeout=30) + yield item + except queue.Empty: + # 发送心跳或检查客户端连接 + if not context.is_active(): + break + finally: + producer_thread.join(timeout=1) +``` + +### 流中的错误处理 +在流式操作中优雅地处理错误: + +```python title="stream_error_handling.py" +def StreamWithErrorHandling(self, request, context): + """带健壮错误处理的流式传输。""" + + try: + for item in self.get_stream_data(request): + if not context.is_active(): + break + + try: + # 处理项目 + processed_item = self.process_item(item) + yield processed_item + + except ProcessingError as e: + # 将错误作为响应的一部分发送 + error_response = create_error_response(e) + yield error_response + + except Exception as e: + # 严重错误 - 中止流 + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Processing failed: {str(e)}') + break + + except Exception as e: + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(f'Stream failed: {str(e)}') +``` + +### 客户端流式传输 + +在客户端处理流式传输: + +```python title="streaming_client.py" +import grpc + +def stream_chat_client(): + """双向流式传输客户端示例。""" + + channel = grpc.insecure_channel('localhost:50051') + stub = streaming_pb2_grpc.StreamingServiceStub(channel) + + def message_generator(): + """生成出站消息。""" + # 发送初始消息 + yield streaming_pb2.ChatMessage( + user_id="user123", + room_id="general", + content="Hello, world!", + type=streaming_pb2.CHAT_MESSAGE_TYPE_TEXT + ) + + # 保持连接活跃并定期发送消息 + while True: + user_input = input("Enter message: ") + if user_input.lower() == 'quit': + break + + yield streaming_pb2.ChatMessage( + user_id="user123", + room_id="general", + content=user_input, + type=streaming_pb2.CHAT_MESSAGE_TYPE_TEXT + ) + + # 启动双向流 + responses = stub.Chat(message_generator()) + + try: + for response in responses: + print(f"Received: {response.content}") + except grpc.RpcError as e: + print(f"RPC failed: {e}") +``` + +gRPC 中的流式传输可实现强大的实时应用程序,同时保持强类型契约和高效二进制协议的优势。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/auth.mdx b/fern/translations/zh/products/api-def/openapi/auth.mdx new file mode 100644 index 000000000..394972b3b --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/auth.mdx @@ -0,0 +1,272 @@ +--- +title: 身份验证 +description: '为 bearer、basic、API 密钥和 OAuth 等认证方案建模。' +sidebar-title: 身份验证 +--- + +Fern 支持两种配置身份验证的方式: + +- **在您的 OpenAPI 规范中**使用 `securitySchemes` — 标准方法,使身份验证配置具有可移植性,并与其他 OpenAPI 工具兼容。 +- **在 `generators.yml` 中**使用 `auth-schemes` — 用于自定义参数名称和环境变量,覆盖规范中定义的内容,或配置 OAuth(在 OpenAPI 中不可用)。 + +您的身份验证配置适用于生成的 SDK 和 [API 浏览器](/learn/docs/api-references/api-explorer)。所有 SDK 都支持凭据的直接配置和环境变量。如果您在两个地方定义了相同的方案,`generators.yml` 优先。 + +## 在您的规范中配置身份验证 + +在 `components.securitySchemes` 中定义您的方案,然后使用 `security` 属性全局或按端点应用它们。 + +```yml title="openapi.yml" +# 定义方案 +components: + securitySchemes: + BearerAuth: # 用户定义的方案名称 + type: http + scheme: bearer + +# 在所有端点中全局应用 +security: + - BearerAuth: [] +``` + +生成的 SDK 用法: + +```ts index.ts +const client = new Client({ + token: "ey34..." +}); +``` + + + + +```yml title="openapi.yml" +components: + securitySchemes: + BearerAuth: # 用户定义的方案名称 + type: http + scheme: bearer +``` + +要自定义参数名称和环境变量,请添加 `x-fern-bearer`: + +```yaml title="openapi.yml" +components: + securitySchemes: + BearerAuth: + type: http + scheme: bearer + x-fern-bearer: + name: apiKey + env: PLANTSTORE_API_KEY +``` + + + + +```yaml title="openapi.yml" +components: + securitySchemes: + BasicAuth: # 用户定义的方案名称 + type: http + scheme: basic +``` + +要自定义参数名称和环境变量,请添加 `x-fern-basic`: + +```yaml title="openapi.yml" +components: + securitySchemes: + BasicAuth: + type: http + scheme: basic + x-fern-basic: + username: + name: clientId + env: PLANTSTORE_CLIENT_ID + password: + name: clientSecret + env: PLANTSTORE_CLIENT_SECRET +``` + + + + +```yml title="openapi.yml" +components: + securitySchemes: + ApiKeyAuth: # 用户定义的方案名称 + type: apiKey + in: header + name: X_API_KEY +``` + +要自定义参数名称和环境变量,请添加 `x-fern-header`: + +```yaml title="openapi.yml" +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X_API_KEY + x-fern-header: + name: apiToken + env: PLANTSTORE_API_KEY + prefix: "Token " +``` + + +`prefix` 选项自动为 API 密钥添加字符串前缀,当您的 API 期望 `"Bearer abc123"` 或 `"Token abc123"` 等格式时很有用。 + + + + + +### 多种身份验证方案 + +配置端点以支持多种身份验证方案或组合。在 `security` 部分中,多个顶级项是 OR 选项,而单个项中的方案用 AND 组合。 + +```yaml title="openapi.yml" +components: + securitySchemes: + bearerAuth: # 用户定义的方案名称 + type: http + scheme: bearer + basicAuth: # 用户定义的方案名称 + type: http + scheme: basic + apiKey: # 用户定义的方案名称 + type: apiKey + in: header + name: X-API-Key + +paths: + /plant/search/status: + get: + summary: 按状态搜索植物 + security: + - bearerAuth: [] # 选项 1:仅 Bearer 令牌 + - basicAuth: [] # 选项 2:基本身份验证和 API 密钥 + apiKey: [] +``` + +在此示例中,用户可以使用 Bearer 令牌或基本身份验证和 API 密钥一起进行身份验证。 + + +当使用 OAuth 客户端凭据与多种方案时,确保您的 OpenAPI 规范 `security` 部分中的方案名称与 `generators.yml` 中定义的名称匹配。 + + +## 在 `generators.yml` 中自定义或覆盖身份验证 + +在 [`auth-schemes`](/learn/sdks/reference/generators-yml#auth-schemes) 中定义您的方案,然后使用 [`api.auth`](/learn/sdks/reference/generators-yml#auth) 将其作为所有端点的默认值应用: + +```yml title="generators.yml" +# 定义方案 +auth-schemes: + BearerAuth: # 用户定义的方案名称 + scheme: bearer + token: + name: apiKey + env: PLANTSTORE_API_KEY + +# 将其作为所有端点的默认值应用 +api: + auth: BearerAuth + specs: + - openapi: ./openapi.yml +``` + +有关完整的配置选项,请参阅 [`auth-schemes` 参考](/learn/sdks/reference/generators-yml#auth-schemes)。您还可以为[特定 SDK](/learn/sdks/reference/generators-yml#override-api-authentication-settings) 覆盖身份验证设置。 + + +生成的 SDK 用法: + +```ts index.ts +// 使用 process.env.PLANTSTORE_API_KEY +const client = new PlantStoreClient(); + +// 或显式提供 +const client = new PlantStoreClient({ + apiKey: "your-api-key" +}); +``` + + + + +```yaml title="generators.yml" +auth-schemes: + BearerAuth: # 用户定义的方案名称 + scheme: bearer + token: + name: apiKey + env: MY_API_KEY +``` + + + + +```yaml title="generators.yml" +auth-schemes: + BasicAuth: # 用户定义的方案名称 + scheme: basic + username: + name: clientId + env: MY_CLIENT_ID + password: + name: clientSecret + env: MY_CLIENT_SECRET +``` + + + + +```yaml title="generators.yml" +auth-schemes: + ApiKeyAuth: # 用户定义的方案名称 + header: X-API-Key + name: apiKey + env: MY_API_KEY + prefix: "Token " +``` + + + + + +此功能在[专业版和企业版计划](https://buildwithfern.com/pricing)中提供。请联系 support@buildwithfern.com 开始使用。 + + +```yaml title="generators.yml" +auth-schemes: + OAuth: # 用户定义的方案名称 + scheme: oauth + type: client-credentials + client-id-env: OAUTH_CLIENT_ID + client-secret-env: OAUTH_CLIENT_SECRET + get-token: + endpoint: "POST /oauth/token" + request-properties: + client-id: client_id + client-secret: client_secret + response-properties: + access-token: access_token + expires-in: expires_in + refresh-token: refresh_token + refresh-token: + endpoint: "POST /oauth/refresh" + request-properties: + refresh-token: refresh_token + response-properties: + access-token: access_token + expires-in: expires_in +``` + +`endpoint` 值引用您的 OpenAPI 规范中的路径。当返回 `expires-in` 时,SDK 会在令牌过期前自动刷新令牌。 + + +如果您的 API 使用[命名空间](/learn/api-definitions/overview/project-structure#combined-sdks-from-multiple-apis)(多个 API 规范),请在端点前加上命名空间和 `::`。例如,`"payments::POST /oauth/token"`。 + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/automation.mdx b/fern/translations/zh/products/api-def/openapi/automation.mdx new file mode 100644 index 000000000..e4bf2b2ee --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/automation.mdx @@ -0,0 +1,63 @@ +--- +title: 同步您的 OpenAPI 规范 +subtitle: 自动将最新的 OpenAPI 规范拉取到您的 Fern 文件夹中。 +sidebar-title: 同步您的 OpenAPI 规范 +--- + +使用 [sync-openapi GitHub Action](https://github.com/fern-api/sync-openapi) 自动从公开可用的 URL 将最新的 OpenAPI 规范拉取到您的 fern 文件夹中。这样可以让您提交的规范与实时 API 保持同步,无需手动更新。 + +## 设置 + + + 在您的 `generators.yml` 中添加 `origin` 字段,指定您的 OpenAPI 规范托管位置: + + ```yml title="generators.yml" + api: + specs: + - openapi: .path/to/openapi.json # 定义文件 + overrides: .path/to-openapi-overrides.yml # 覆盖文件 + origin: https://api.example.com/openapi.json # 获取最新规范的 URL + ``` + + + 在您的仓库中创建 `.github/workflows/sync-openapi.yml`。此操作使用 [`fern api update`](/cli-api-reference/cli-reference/commands#fern-api-update) 从您的 `generators.yml` 文件中的 `origin` 字段拉取最新版本的 OpenAPI 规范。 + + ```yml title=".github/workflows/sync-openapi.yml" + name: Sync OpenAPI Specs # 可以自定义 + on: # 可以配置其他自定义触发器 + workflow_dispatch: # 手动触发 + push: + branches: + - main # 推送到主分支时触发 + schedule: + - cron: '0 3 * * *' # 每日 UTC 时间凌晨 3:00 + jobs: + update-from-source: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.OPENAPI_SYNC_TOKEN }} + - name: Update API with Fern + uses: fern-api/sync-openapi@v2 + with: + update_from_source: true + token: ${{ secrets.OPENAPI_SYNC_TOKEN }} + branch: 'update-api' + auto_merge: false + add_timestamp: true +``` + + + 生成一个 [细粒度个人访问令牌](https://github.com/settings/personal-access-tokens),为您的仓库授予 **Contents** 和 **Pull requests** 读写权限。 + + + 在您的仓库中,转到 `Settings > Secrets and variables > Actions`。点击 **New repository secret**,将其命名为 `OPENAPI_SYNC_TOKEN`,粘贴您的令牌,然后点击 **Add secret**。 + + + +这将创建包含任何 API 规范更新的每日拉取请求。要更改频率,请修改 `cron` 计划(参见 GitHub 的 [计划语法](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule))。 + +## 其他用例 + +如果您的 OpenAPI 规范位于不同的仓库中(而不是在公共 URL),您可以使用显式文件映射将其同步到您的 Fern 文件夹。有关此操作和其他高级配置,请参见 [sync-openapi GitHub Action README](https://github.com/fern-api/sync-openapi)。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/endpoints/multipart.mdx b/fern/translations/zh/products/api-def/openapi/endpoints/multipart.mdx new file mode 100644 index 000000000..c5baa9abf --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/endpoints/multipart.mdx @@ -0,0 +1,74 @@ +--- +title: 多部分文件上传 +subtitle: 使用 `multipart/form-data` 内容类型记录端点 +sidebar-title: 多部分文件上传 +--- + +多部分请求将一个或多个数据集组合到单个请求体中,通过边界分隔。 +您通常使用这些请求进行文件上传,以及在单个请求中传输多种类型的数据 +(例如,文件和 JSON 对象一起传输)。 + +```yml title="openapi.yml" maxLines=0 {12-24} +paths: + /upload: + post: + summary: Upload a file + description: Upload a file using multipart/form-data encoding + operationId: uploadFile + tags: + - file + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: The file to upload + description: + type: string + description: A description of the file (optional) + required: + - file + responses: + "200": + description: Successful upload + content: + application/json: + schema: + type: object + properties: + message: + type: string + fileId: + type: string +``` + +任何使用 `multipart/form-data` 内容类型定义的请求体都将被视为多部分请求。在给定的多部分请求中,具有 `format:binary` 的字符串参数将表示任意文件。 + +## 文件数组 + +如果您的端点支持文件数组,那么您的请求体必须使用数组类型。 + +```yml openapi.yml {12-17} +paths: + /upload: + post: + summary: Upload multiple files + operationId: uploadFiles + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + files: + type: array + items: + type: string + format: binary + description: An array of files to upload +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/endpoints/rest.mdx b/fern/translations/zh/products/api-def/openapi/endpoints/rest.mdx new file mode 100644 index 000000000..4d1f631d4 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/endpoints/rest.mdx @@ -0,0 +1,65 @@ +--- +title: HTTP JSON 端点 +subtitle: 使用 `application/json` 内容类型记录 HTTP JSON API +sidebar-title: HTTP JSON 端点 +--- + +OpenAPI 中的端点定义在 `paths` 键下。以下是定义单个端点的示例: + +```yml title="openapi.yml" maxLines=0 {2-18} +paths: + /pets: + post: + summary: Create a new pet + description: Creates a new pet with the provided information + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + '200': + description: User created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' +``` + +## 示例 + +您可以通过使用 `examples` 键来提供请求和响应的示例。 + +```yaml title="openapi.yml" {12-17,25-30} +paths: + /pets: + post: + summary: Create a new pet + description: Creates a new pet with the provided information + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + examples: + PetExample: + summary: This is an example of a Pet + value: + name: Markley + id: 44 + responses: + '200': + description: A Pet object + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + examples: + PetExample: + summary: This is an example of a Pet + value: + name: Markley + id: 44 +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/endpoints/sse.mdx b/fern/translations/zh/products/api-def/openapi/endpoints/sse.mdx new file mode 100644 index 000000000..da676eade --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/endpoints/sse.mdx @@ -0,0 +1,124 @@ +--- +title: 服务器发送事件和流式 API +subtitle: 使用 `x-fern-streaming` 扩展来建模流式端点 +sidebar-title: 服务器发送事件和流式 API +--- + + +`x-fern-streaming` 扩展允许您表示流式端点。 + +## JSON 流式传输 + +如果您的 API 返回一系列 `JSON` 块,如下所示 + +```json +{ "text": "Hi, I am a" } +{ "text": "chatbot. Do you have any"} +{ "text": "questions for me"} +``` + +那么只需在您的 OpenAPI 操作中添加 `x-fern-streaming: true`。 + +```yaml title="openapi.yml" {4} +paths: + /logs: + post: + x-fern-streaming: true + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Chat" +components: + schemas: + Chat: + type: object + properties: + text: + type: string +``` + +## 服务器发送事件 + + + +如果您的 API 返回服务器发送事件 (SSE),包含 `data` 和 `event` 键,如下所示 + +```json +data: { "text": "Hi, I am a" } +data: { "text": "chatbot. Do you have any"} +data: { "text": "questions for me"} +``` + +那么确保包含 `format: sse`。 + +```yaml title="openapi.yml" {4-5} +paths: + /logs: + post: + x-fern-streaming: + format: sse + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Chat" +components: + schemas: + Chat: + type: object + properties: + text: + type: string +``` + +### 终止消息 + +一些 SSE API 会发送独立的终止消息来表示流传输完成。例如, +OpenAI 的 API 发送 `[DONE]` 作为最终消息。您可以使用 `terminator` 字段来指定: + +```yaml title="openapi.yml" {4-6} +paths: + /logs: + post: + x-fern-streaming: + format: sse + terminator: "[DONE]" +# ... responses and schemas +``` + +## `Stream` 参数 + +端点拥有一个 `stream` 参数来控制响应是否为流式传输已成为常见做法。Fern 以一等公民的方式支持这种模式。 + +只需指定 `stream-condition` 以及普通响应和流式响应: + +```yaml title="openapi.yml" {4-11} +paths: + /logs: + post: + x-fern-streaming: + format: sse + terminator: "[DONE]" + stream-condition: $request.stream + response: + $ref: '#/components/schemas/Chat' + response-stream: + $ref: '#/components/schemas/ChatChunk' +components: + schemas: + Chat: + type: object + properties: + text: + type: string + tokens: + type: number + ChatChunk: + type: object + properties: + text: + type: string +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/api-version.mdx b/fern/translations/zh/products/api-def/openapi/extensions/api-version.mdx new file mode 100644 index 000000000..690b695f4 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/api-version.mdx @@ -0,0 +1,19 @@ +--- +title: API 版本 +headline: API 版本 (OpenAPI) +description: 使用 `x-fern-version` 扩展配置 API 版本方案和请求头 +sidebar-title: API 版本 +--- + +您可以定义您的 API 版本方案,例如 `X-API-Version` 请求头。支持的版本和默认值按如下方式指定: + +```yaml title="openapi.yaml" +x-fern-version: + header: X-API-Version + default: "2.0.0" + values: + - "1.0.0" + - "2.0.0" + - "latest" +paths: ... +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/audiences.mdx b/fern/translations/zh/products/api-def/openapi/extensions/audiences.mdx new file mode 100644 index 000000000..4c347fcf7 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/audiences.mdx @@ -0,0 +1,148 @@ +--- +title: 使用受众筛选您的 API +headline: 使用受众筛选您的 API (OpenAPI) +subtitle: 了解如何使用 x-fern-audiences 为不同的 API 消费者(如公共用户和 beta 用户)筛选 OpenAPI 端点、模式和属性。 +sidebar-title: 使用受众筛选 API +--- + + + + +Fern 提供两种方式来筛选哪些 API 元素会出现在您的 SDK 和 API 参考文档中: + +- **`audiences`(基于标签的筛选):** 为特定端点、模式或属性标记受众标签,然后将 SDK 和文档筛选到这些标签。最适合基于用户类型(公共 vs beta、免费 vs 企业版)或功能成熟度创建变体,无论 URL 结构如何。 + +- **`settings.filter`(基于路径的筛选):** 筛选整个 URL 路径或模式。最适合您的 API 组织自然按 URL 划分的情况(如 `/v1/*` vs `/v2/*`,或 `/admin/*` vs `/user/*`)。配置详细信息请参见 [`settings.filter` 参考](/learn/sdks/reference/generators-yml#openapi)。 + +许多团队同时使用这两种方法:基于路径的筛选用于主要划分,受众标签用于这些划分内的精细控制。 + +## 基于路径的筛选 + +在您的 `generators.yml` 中使用 [`settings.filter`](/learn/api-definitions/openapi/generators-yml-reference#settingsfilter) 来限制哪些端点会根据其路径包含在生成的 SDK 或 API 参考中: + +```yaml title="generators.yml" +api: + specs: + - openapi: "./openapi.yml" + settings: + filter: + endpoints: ["POST /users", "GET /users/{id}"] +``` + +## 基于标签的筛选(受众) + +在您的 OpenAPI 规范中应用受众标签,然后配置您的 SDK 或 API 参考来筛选到这些受众。 + + + + + + + +将 `x-fern-server-name` 和 `x-fern-audiences` 扩展添加到相关服务器。 + +在下面的示例中,`Production` 服务器仅对公共消费者可用: + +```yaml title="openapi.yml" {3-5} +servers: + - url: https://api.com + x-fern-server-name: Production + x-fern-audiences: + - public +``` + + + +将 `x-fern-audiences` 扩展添加到相关端点。 + +在下面的示例中,`POST /users/sendEmail` 端点仅对公共消费者可用: + +```yaml title="openapi.yml" {4-5} +paths: + /users/sendEmail: + post: + x-fern-audiences: + - public + operationId: send_email +``` + + + +将 `x-fern-audiences` 扩展添加到相关模式。 + +在此示例中,`Email` 类型对公共和 beta 客户都可用。 + +```yaml title="openapi.yml" {13-15} +components: + schemas: + Email: + title: Email + type: object + properties: + subject: + type: string + body: + type: string + to: + type: string + x-fern-audiences: + - public + - beta +``` + + + +将 `x-fern-audiences` 扩展添加到相关属性。 + +在此示例中,`to` 属性仅对 beta 客户可用。 + +```yaml title="openapi.yml" {13-17} +components: + schemas: + Email: + title: Email + type: object + properties: + subject: + type: string + body: + type: string + to: + type: string + x-fern-audiences: + - beta +``` + + + + + +指定要包含在您的 SDK 或 API 参考文档中的受众。如果**没有指定受众**,所有标记的元素都将包含在您的 SDK 中,无论其受众标签如何。 + + + +以下示例配置 SDK 筛选到 `public` 受众: + +```yaml title="generators.yml" {3-4} +groups: + sdks: + audiences: + - public + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + + +以下示例配置文档筛选到 `public` 受众: + +```yaml title="docs.yml" {3-4} +navigation: + - api: API Reference + audiences: + - public +``` + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/availability.mdx b/fern/translations/zh/products/api-def/openapi/extensions/availability.mdx new file mode 100644 index 000000000..745f72f70 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/availability.mdx @@ -0,0 +1,38 @@ +--- +title: 可用性 +headline: 可用性 (OpenAPI) +sidebar-title: 可用性 +description: 在 OpenAPI 中使用 `x-fern-availability` 标记 API 端点可用性。为端点和章节设置 beta、已弃用、稳定等状态。 +--- + + +`x-fern-availability` 扩展用于在 OpenAPI 定义中标记端点的可用性。可用性信息会传播到生成的 Fern Docs 网站中,显示为可视化标签。 + +您可以在 [`docs.yml` 文件](/learn/docs/configuration/site-level-settings)中配置 API Reference 文档中章节的 `availability`。 + +## 端点 + +选项包括: + +- `beta` +- `generally-available` +- `deprecated` + +下面的示例将 `POST /pet` 端点标记为 `deprecated`。 + +```yaml title="openapi.yml 中的 x-fern-availability" {4} +paths: + /pet: + post: + x-fern-availability: deprecated +``` + +渲染效果如下: + + +![显示已弃用标签的 API Reference 端点截图](https://fern-image-hosting.s3.amazonaws.com/fern/x-fern-availability-example.png) + + +## 章节 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/base-path.mdx b/fern/translations/zh/products/api-def/openapi/extensions/base-path.mdx new file mode 100644 index 000000000..3fc8c0a00 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/base-path.mdx @@ -0,0 +1,19 @@ +--- +title: 基础路径 +headline: 基础路径(OpenAPI) +description: 使用 `x-fern-base-path` 在 OpenAPI 中配置基础路径。为您的 API 定义设置端点前缀,如 /v1,配合 Fern 使用。 +sidebar-title: 基础路径 +--- + +使用 `x-fern-base-path` 扩展来配置添加到每个端点前面的基础路径。 + +下面的示例配置了 `/v1` 基础路径,因此完整的端点路径是 +`https://api.example.com/v1/users`。 + +```yaml title="在 openapi.yml 中设置基础路径" {1} +x-fern-base-path: /v1 +servers: + - url: https://api.example.com +paths: + /users: ... +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/default.mdx b/fern/translations/zh/products/api-def/openapi/extensions/default.mdx new file mode 100644 index 000000000..e35c234be --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/default.mdx @@ -0,0 +1,77 @@ +--- +title: 默认值 +headline: 默认值 (OpenAPI) +description: 使用 `x-fern-default` 为生成的 SDK 中的路径、头部和查询参数设置客户端默认值。 +sidebar-title: 默认值 +--- + +`x-fern-default` 扩展允许您为路径、头部或查询参数指定客户端默认值,包括在 [`x-fern-global-headers`](/learn/api-definitions/openapi/extensions/global-headers) 下定义的头部。当存在时,生成的 SDK 会使参数变为可选的,并在调用方省略该参数时自动发送默认值。`x-fern-default` 支持 `string` 和 `boolean` 值;其他类型(如数字)会被忽略。 + +这对于固定 API 版本头部或区域路径参数非常有用,同时仍允许调用方覆盖该值。 + + + `x-fern-default` 支持 TypeScript、Python、Go、Java、C#、PHP 和 Ruby SDK。 + + +## 路径参数 + +在下面的示例中,当调用方未指定 `region` 时,SDK 会发送 `us-east-1`。 + +```yaml {9} title="openapi.yml" +paths: + /regions/{region}/resources: + get: + operationId: list_resources + parameters: + - name: region + in: path + required: true + x-fern-default: "us-east-1" + schema: + type: string +``` + +## 头部 + +在下面的示例中,当调用方未指定 `X-API-Version` 时,SDK 会发送 `2024-02-08`。 + +```yaml {8} title="openapi.yml" +paths: + /users: + get: + operationId: list_users + parameters: + - name: X-API-Version + in: header + x-fern-default: "2024-02-08" + schema: + type: string +``` + +## 查询参数 + +在下面的示例中,当调用方未指定 `verbose` 时,SDK 会发送 `false`。 + +```yaml {8} title="openapi.yml" +paths: + /search: + get: + operationId: search + parameters: + - name: verbose + in: query + x-fern-default: false + schema: + type: boolean +``` + +## 全局头部 + +在下面的示例中,当调用方未指定 `X-API-Version` 全局头部时,SDK 会发送 `2024-02-08`。 + +```yaml {4} title="openapi.yml" +x-fern-global-headers: + - header: X-API-Version + name: version + x-fern-default: "2024-02-08" +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/enums.mdx b/fern/translations/zh/products/api-def/openapi/extensions/enums.mdx new file mode 100644 index 000000000..79833b4bd --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/enums.mdx @@ -0,0 +1,97 @@ +--- +title: 枚举描述、名称、大小写和可用性 +headline: 枚举描述、名称、大小写和可用性(OpenAPI) +description: 使用 `x-fern-enum` 扩展为 OpenAPI 枚举值添加描述、自定义名称、按语言的大小写覆盖和弃用状态。 +sidebar-title: 枚举描述、名称、大小写和可用性 +--- + +OpenAPI 原生不支持为枚举值添加描述。要在 Fern 中实现此功能,您可以使用 `x-fern-enum` 扩展。 + +## 描述和可用性 + +使用 `description` 为单个枚举值添加文档,使用 `deprecated` 将值标记为已弃用而不会弃用整个枚举。这些设置会传播到生成的 SDK 和文档网站中。 + +```yaml title="openapi.yml" {11, 13-14, 16, 18} +components: + schemas: + CardSuit: + enum: + - clubs + - diamonds + - hearts + - spades + x-fern-enum: + clubs: + description: 梅花花色 + diamonds: + description: "已弃用。请使用红心。" + deprecated: true + hearts: + description: 红心花色 + spades: + description: 黑桃花色 +``` + +## 自定义名称 + +使用 `name` 字段自定义生成代码中枚举值的名称。当枚举值依赖符号字符而可能导致生成的代码无法编译时,这特别有用。 + +例如,以下 OpenAPI: + +```yaml title="openapi.yml" {9,12} +components: + schemas: + Operand: + enum: + - '>' + - '<' + x-fern-enum: + '>': + name: GreaterThan + description: 检查值是否大于 + '<': + name: LessThan + description: 检查值是否小于 +``` + +将生成: + +```typescript title="operand.ts" +export enum Operand { + GreaterThan = ">", + LessThan = "<" +} +``` + +## 自定义大小写 + +使用 `casing` 字段为每个目标语言的命名约定指定确切的大小写。这比仅使用 `name` 提供了更精细的控制,后者只为生成的代码设置单个默认名称。 + +`casing` 字段支持四个可选的子字段: + +- `snake` — snake_case 的覆盖(用于 Python 等语言) +- `camel` — camelCase 的覆盖(用于 TypeScript/Java 等语言) +- `screamingSnake` — SCREAMING_SNAKE_CASE 的覆盖(用于 Go 等语言) +- `pascal` — PascalCase 的覆盖(用于 C# 等语言) + +```yaml title="openapi.yml" {13-17} +components: + schemas: + Status: + type: string + enum: + - active + - in-progress + x-fern-enum: + active: + description: 项目处于活动状态 + in-progress: + name: InProgress + casing: + snake: in_progress + camel: inProgress + screamingSnake: IN_PROGRESS + pascal: InProgress +``` + +您可以将 `casing` 与 `name` 一起使用。`name` 字段设置默认生成的名称,而 `casing` 提供按语言的覆盖。如果两者都指定,`casing` 值对其各自的语言目标具有优先级。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/examples.mdx b/fern/translations/zh/products/api-def/openapi/extensions/examples.mdx new file mode 100644 index 000000000..0fa22079b --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/examples.mdx @@ -0,0 +1,74 @@ +--- +title: 请求 + 响应示例 +headline: 请求 + 响应示例(OpenAPI) +description: 使用 x-fern-examples 配置 OpenAPI 请求和响应示例。在您的 API 定义中关联路径参数、查询参数和响应。 +sidebar-title: 请求 + 响应示例 +og:title: OpenAPI 请求 + 响应示例 +--- + + + +Fern 使用 [AI 生成的示例](/learn/docs/ai-features/ai-examples) 自动生成真实的示例,默认启用。使用 `x-fern-examples` 手动定义特定的示例值。手动示例优先于 AI 生成的示例。您也可以[完全禁用 AI 示例](/learn/docs/configuration/site-level-settings#ai-examplesenabled)。 + + +当您需要关联特定的请求和响应对,或为端点定义多个命名示例时,请使用 `x-fern-examples`。虽然 OpenAPI 有多个示例字段,但它无法将请求与其对应的响应关联起来。如果您需要您的示例与非 Fern 的 OpenAPI 工具兼容,请使用 [`fern api enrich`](/learn/cli-api-reference/cli-reference/commands#fern-api-enrich) 将 `x-fern-examples` 转换为原生 OpenAPI 示例字段。 + +`x-fern-examples` 是一个数组,其中每个元素可以包含关联的 `path-parameters`、`query-parameters`、`request` 和 `response` 值。可选地,添加 `name` 字段为每个示例提供描述性标签。 + +如果您[通过 `x-fern-global-headers` 扩展定义了全局标头](/api-definitions/openapi/extensions/global-headers),您必须在示例中包含这些标头。 + +```yaml title="openapi.yml" {4-21} +paths: + /users/{userId}: + get: + x-fern-examples: + - name: Headers example # 可选的描述性标签 + headers: + custom_api_key: "capi_12345" # 使用 x-global-header 扩展定义的标头 + userpool_id: "pool_67890" # 使用 x-global-header 扩展定义的标头 + - name: Get user 1234 + path-parameters: + userId: user-1234 + response: + body: + name: Foo + ssn: 1234 + - path-parameters: + userId: user-4567 + response: + body: + name: Foo + ssn: 4567 +components: + schemas: + User: + type: object + properties: + name: + type: string + ssn: + type: integer +``` + +### 代码示例 + +Fern 生成器会自动添加 SDK 代码示例。如果您想为示例指定自定义代码示例,请使用 `code-samples`。 + +```yaml title="openapi.yml" {11-16} +paths: + /users/{userId}: + get: + x-fern-examples: + - path-parameters: + userId: user-1234 + response: + body: + name: Foo + ssn: 1234 + code-samples: + - sdk: typescript + code: | + import { UserClient } from "..."; + + client.users.get("user-1234") +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/explorer.mdx b/fern/translations/zh/products/api-def/openapi/extensions/explorer.mdx new file mode 100644 index 000000000..2adceddb5 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/explorer.mdx @@ -0,0 +1,59 @@ +--- +title: API Explorer 控制 +headline: API Explorer 控制 (OpenAPI) +description: 使用 `x-fern-explorer` 启用或禁用 API Explorer +sidebar-title: API Explorer 控制 +--- + +[API Explorer](/learn/docs/api-references/api-explorer) 默认为所有端点启用。使用 `x-fern-explorer` 全局禁用或按操作覆盖。这通常用于对破坏性操作、支付处理或仅限管理员的端点禁用 Explorer。 + +## 全局 + +要为整个 API 禁用 API Explorer,请在根级别添加 `x-fern-explorer`: + +```yaml title="openapi.yml" {2} +openapi: 3.0.2 +x-fern-explorer: false # 为所有端点禁用 Explorer +info: + title: My API + version: 1.0.0 +paths: + /payments: + get: + operationId: list_payments +``` + +## 端点 + +要控制各个端点的 API Explorer,请使用 `x-fern-explorer`: + +```yaml title="openapi.yml" {4,9} +paths: + /payments: + get: + x-fern-explorer: true # 为安全的读取操作启用 Explorer + /payments/charge: + post: + x-fern-explorer: false # 禁用以防止意外交易 +``` + +## 结合全局和端点级别设置 + +您可以结合使用两种扩展来全局设置默认行为并为特定端点覆盖: + +```yaml title="openapi.yml" {2,10} +openapi: 3.0.2 +x-fern-explorer: false # 全局禁用 Explorer +info: + title: My API + version: 1.0.0 +paths: + /payments: + get: + operationId: list_payments + x-fern-explorer: true # 为安全的读取操作启用 Explorer + /payments/charge: + post: + operationId: charge_payment + # 保持禁用(应用全局设置) +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/global-headers.mdx b/fern/translations/zh/products/api-def/openapi/extensions/global-headers.mdx new file mode 100644 index 000000000..f2ab7cbbf --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/global-headers.mdx @@ -0,0 +1,73 @@ +--- +title: 全局请求头 +headline: 全局请求头 (OpenAPI) +description: 使用 `x-fern-global-headers` 扩展配置在所有端点中使用的请求头 +sidebar-title: 全局请求头 +--- + +有时,您的 API 会在每个端点或大部分端点中使用某些请求头,我们称之为"全局请求头"。为了方便起见,生成的 Fern SDK 提供了"全局请求头"功能,可以轻松地在 API 调用中更新它们。以 API 密钥为例,如果我们将 API 密钥声明为全局请求头,用户将能够轻松地插入他们的密钥: + +```python +import os + +class Client: + + def __init__(self, *, apiKey: str): +``` + +Fern 会自动提取在每个请求或大多数请求中使用的请求头,并将它们标记为全局的。 + +## 在您的 OpenAPI 规范中 + +要将其他请求头标记为全局的,或为全局请求头设置别名,请使用 `x-fern-global-headers` 扩展: + +```yaml title="openapi.yml" +x-fern-global-headers: + - header: custom_api_key + name: api_key + - header: userpool_id + optional: true +``` + + + 当您使用 `x-fern-global-headers` 定义全局请求头时,您必须[在 `x-fern-examples` 中包含它们](/api-definitions/openapi/extensions/request-response-examples)。 + + +### 默认值 + +使用 [`x-fern-default`](/learn/api-definitions/openapi/extensions/default-values) 为全局请求头设置客户端默认值。生成的 SDK 会将请求头设置为可选,并在调用者省略时发送默认值: + +```yaml {4} title="openapi.yml" +x-fern-global-headers: + - header: X-API-Version + name: version + x-fern-default: "2024-02-08" +``` + +## 在 `generators.yml` 中 + +或者,您可以将请求头添加到[`generators.yml` 文件中的 `api` 块](/learn/sdks/reference/generators-yml#headers): + +```yaml title="generators.yml" +api: + - openapi: ./path/to/openapi + headers: + custom_api_key: + name: api_key + type: string + userpool_id: + name: userpool_id + type: optional +``` + +## 生成的 SDK 行为 + +两种配置都会产生以下客户端代码: + +```python +import os + +class Client: + + def __init__(self, *, apiKey: str, userpoolId: typing.Optional[str]) +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/idempotency-headers.mdx b/fern/translations/zh/products/api-def/openapi/extensions/idempotency-headers.mdx new file mode 100644 index 000000000..cfa872f40 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/idempotency-headers.mdx @@ -0,0 +1,60 @@ +--- +title: 幂等性 +headline: 幂等性 (OpenAPI) +description: 使用 x-fern-idempotency-headers 和 x-fern-idempotent 扩展配置幂等性,实现安全的请求重试 +sidebar-title: 幂等性 +--- + + + + +Fern 通过两个协同工作的扩展支持幂等性配置:`x-fern-idempotency-headers` 配置您的 API 使用的幂等性头部,`x-fern-idempotent` 将单个端点标记为幂等的。配置后,Fern 生成的 SDK 允许用户指定幂等性头部以进行安全的请求重试。 + +## 配置幂等性头部 + +在 OpenAPI 规范的根级别添加 `x-fern-idempotency-headers` 扩展,以定义您的 API 用于幂等性的头部: + +```yaml title="openapi.yml" +x-fern-idempotency-headers: + - header: Idempotency-Key + name: idempotency_key + - header: Idempotency-Expiration + name: idempotency_expiration +``` + +每个幂等性头部支持以下属性: + +| 属性 | 描述 | +| --- | --- | +| `header` | HTTP 头部名称(例如:`Idempotency-Key`) | +| `name` | 生成的 SDK 中使用的参数名称(例如:`idempotency_key`) | + +## 将端点标记为幂等的 + +在单个端点上添加 `x-fern-idempotent` 扩展并将其设置为 `true`,以将它们标记为幂等的: + +```yaml title="openapi.yml" {1-3,8} +x-fern-idempotency-headers: + - header: Idempotency-Key + name: idempotency_key + +paths: + /plants: + post: + x-fern-idempotent: true + operationId: create_plant + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + species: + type: string + responses: + '201': + description: Plant created successfully +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/ignore.mdx b/fern/translations/zh/products/api-def/openapi/extensions/ignore.mdx new file mode 100644 index 000000000..18f6c32c9 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/ignore.mdx @@ -0,0 +1,74 @@ +--- +title: 忽略元素 +headline: 忽略元素 (OpenAPI) +description: 使用 x-fern-ignore 从您的 OpenAPI 规范中排除端点、模式、属性或参数。控制 Fern 读取和处理的内容。 +sidebar-title: 忽略元素 +--- + +如果您希望 Fern 跳过读取任何端点、模式、属性或参数,请使用 `x-fern-ignore` 扩展。 + +## 忽略端点 + +要跳过端点,请在操作级别添加 `x-fern-ignore: true`。 + +```yaml title="openapi.yml 中操作级别的 x-fern-ignore" {4} +paths: + /users: + get: + x-fern-ignore: true + ... +``` +## 忽略模式 + +要跳过模式,请在模式级别添加 `x-fern-ignore: true`。 + +```yaml title="openapi.yml 中模式级别的 x-fern-ignore" {4} +components: + schemas: + SchemaToSkip: + x-fern-ignore: true + ... +``` + +## 忽略属性 +要跳过模式中的属性,请在属性级别添加 `x-fern-ignore: true`。 + +```yaml title="openapi.yml 中属性级别的 x-fern-ignore" {9} +components: + schemas: + User: + type: object + properties: + name: + type: string + internalField: + x-fern-ignore: true + type: string +``` + +## 忽略参数 + +要跳过参数,请在参数级别添加 `x-fern-ignore: true`。 +```yaml title="openapi.yml 中参数级别的 x-fern-ignore" {7} +paths: + /users: + get: + parameters: + - name: internalParam + in: query + x-fern-ignore: true + schema: + type: string +``` + + +要在不修改基础规范的情况下跳过参数,请在您的[覆盖文件](/learn/api-definitions/openapi/overrides)中使用 `null` 值来删除参数: + +```yaml title="overrides.yml" {5} +paths: + /users: + get: + parameters: + - null # 从基础规范中删除第一个参数 +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/method-names.mdx b/fern/translations/zh/products/api-def/openapi/extensions/method-names.mdx new file mode 100644 index 000000000..b102d2922 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/method-names.mdx @@ -0,0 +1,53 @@ +--- +title: 自定义 SDK 方法名 +headline: 自定义 SDK 方法名 (OpenAPI) +description: 使用 `x-fern-sdk-method-name` 和 `x-fern-sdk-group-name` 来精细调整 SDK 命名。 +sidebar-title: 自定义 SDK 方法名 +--- + +使用 `x-fern-sdk-group-name` 和 `x-fern-sdk-method-name` 扩展来控制端点在 SDK 中的组织方式。 + + + 如果没有扩展存在,Fern 使用您的操作 ID 来生成 SDK 方法名。将操作 ID 格式化为 `{tag_name}_{operation_name}`(例如:`users_get`)来自动生成像 `users.get()` 这样的方法。如果操作 ID 不以标签开头,Fern 将直接使用它作为方法名。 + + +在下面的示例中,Fern 将为 `POST /users` 端点生成一个名为 `client.users.create()` 的方法。 + +```yaml title="openapi.yaml" {4-5} +paths: + /users: + post: + x-fern-sdk-group-name: users + x-fern-sdk-method-name: create +``` + +### 顶级方法 + +如果您省略 `x-fern-sdk-group-name` 扩展,生成的 SDK 方法将位于客户端的根级别,而不是嵌套在资源组下。在下面的示例中,Fern 将生成一个名为 `client.send()` 的方法: + +```yaml title="openapi.yaml" {4} +paths: + /send: + post: + x-fern-sdk-method-name: send +``` + +### 多级嵌套 + + +查看 merge.dev 如何使用嵌套组 [这里](https://github.com/merge-api/merge-node-client?tab=readme-ov-file#create-link-token)。 + + +如果您添加多个 `x-fern-sdk-group-name` 扩展,那么生成的 SDK 将嵌套组名。生成的 SDK 方法保持组名的顺序。 + +在下面的示例中,Fern 将生成一个名为 `client.users.notifications.send()` 的方法: + +```yaml title="openapi.yaml" +paths: + /users/notifications: + post: + x-fern-sdk-group-name: + - users + - notifications + x-fern-sdk-method-name: send +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/pagination.mdx b/fern/translations/zh/products/api-def/openapi/extensions/pagination.mdx new file mode 100644 index 000000000..8ebeeff7a --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/pagination.mdx @@ -0,0 +1,151 @@ +--- +title: 分页 +headline: 分页 (OpenAPI) +description: 使用 x-fern-pagination 扩展为列表端点配置自动分页 +sidebar-title: 分页 +--- + + + + +`x-fern-pagination` 扩展为 OpenAPI 规范中的列表端点配置自动分页。[Fern 生成的 SDK](/learn/sdks/deep-dives/auto-pagination) 提供简单的迭代器,可以自动处理分页,因此 SDK 用户可以遍历所有结果,而无需手动管理分页复杂性。 + +要配置分页: + +1. 使用 `x-fern-pagination` 扩展注解所需的分页端点 +2. 指定分页方案(`offset`、`cursor`、`next_uri` 或 `next_path`) +3. 使用点访问表示法指定 `results` 的位置 + + +```yaml title="偏移分页" {5-7} +paths: + /plants: + get: + operationId: list_plants + x-fern-pagination: + offset: $request.page_number + results: $response.results + parameters: + - name: page_number + in: query + schema: + type: integer + responses: + '200': + description: List of plants + content: + application/json: + schema: + type: object + properties: + results: + type: array + items: + $ref: '#/components/schemas/Plant' +``` + +```yaml title="游标分页" {5-8} +paths: + /plants: + get: + operationId: list_plants + x-fern-pagination: + cursor: $request.cursor + next_cursor: $response.next + results: $response.results + parameters: + - name: cursor + in: query + schema: + type: string + responses: + '200': + description: List of plants + content: + application/json: + schema: + type: object + properties: + next: + type: string + results: + type: array + items: + $ref: '#/components/schemas/Plant' +``` + +```yaml title="URI 分页" {5-7} +paths: + /plants: + get: + operationId: list_plants + x-fern-pagination: + next_uri: $response.next_page_url + results: $response.results + responses: + '200': + description: List of plants + content: + application/json: + schema: + type: object + properties: + next_page_url: + type: string + nullable: true + results: + type: array + items: + $ref: '#/components/schemas/Plant' +``` + +```yaml title="路径分页" {5-7} +paths: + /plants: + get: + operationId: list_plants + x-fern-pagination: + next_path: $response.next_page_path + results: $response.results + responses: + '200': + description: List of plants + content: + application/json: + schema: + type: object + properties: + next_page_path: + type: string + nullable: true + results: + type: array + items: + $ref: '#/components/schemas/Plant' +``` + + +## 配置选项 + +`x-fern-pagination` 扩展支持以下属性: + + + + + +如果您的结果嵌套在响应对象中,请使用点访问表示法指定路径。例如,如果结果位于 `my_nested_object.inner_list` 中,`results` 路径将是 `$response.my_nested_object.inner_list`。 + +```yaml {4, 7} +MyResponseObject: + type: object + properties: + my_nested_object: + type: object + properties: + inner_list: + type: array + items: + $ref: '#/components/schemas/Plant' +``` + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/parameter-names.mdx b/fern/translations/zh/products/api-def/openapi/extensions/parameter-names.mdx new file mode 100644 index 000000000..7519fe35c --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/parameter-names.mdx @@ -0,0 +1,65 @@ +--- +title: 自定义参数名称 +headline: 自定义参数名称 (OpenAPI) +description: 使用 `x-fern-parameter-name` 来自定义查询参数、请求头和路径参数的命名。 +sidebar-title: 自定义参数名称 +--- + + + + `x-fern-parameter-name` 扩展允许您在生成的 SDK 中自定义参数的变量名称。 + + +## 请求头 + +在下面的示例中,请求头 `X-API-Version` 在生成的 SDK 中被重命名为 `version`。这种重命名使 SDK 更具可读性。 + +```yaml {8} +paths: + "/user": + get: + operationId: list_user + parameters: + - in: header + name: X-API-Version + x-fern-parameter-name: version + schema: + type: string + required: true +``` + +## 查询参数 + +在下面的示例中,查询参数 `q` 在生成的 SDK 中被重命名为 `search_terms`。这种重命名使参数对最终用户更加友好。 + +```yaml {8} +paths: + "/user/search": + get: + operationId: search_user + parameters: + - in: query + name: q + x-fern-parameter-name: search_terms + schema: + type: string + required: false +``` + +## 路径参数 + +在下面的示例中,路径参数 `userId` 在生成的 SDK 中被重命名为 `id`。这种重命名使 SDK 更加简洁。 + +```yaml {8} +paths: + "/user/{userId}": + get: + operationId: get_user + parameters: + - in: path + name: userId + x-fern-parameter-name: id + schema: + type: string + required: false +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/property-names.mdx b/fern/translations/zh/products/api-def/openapi/extensions/property-names.mdx new file mode 100644 index 000000000..e8c7a0f5d --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/property-names.mdx @@ -0,0 +1,19 @@ +--- +title: 属性名称 +headline: 属性名称 (OpenAPI) +description: 使用 `x-fern-property-name` 扩展自定义对象属性的变量名称 +sidebar-title: 属性名称 +--- + +`x-fern-property-name` 扩展允许您自定义对象属性的变量名称。 + +例如,如果您的模式中有一个名为 `_metadata` 的属性,但您希望在 SDK 中将变量称为 `data`,您可以执行以下操作: + +```yaml {6} +components: + schemas: + MyUser: + _metadata: + type: object + x-fern-property-name: data +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/retries.mdx b/fern/translations/zh/products/api-def/openapi/extensions/retries.mdx new file mode 100644 index 000000000..26a9c9f40 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/retries.mdx @@ -0,0 +1,51 @@ +--- +title: 重试行为 +sidebar-title: 重试行为 +headline: 重试行为 (OpenAPI) +description: 使用 x-fern-retries 扩展控制 API 重试逻辑。配置端点级别的重试行为,并为支付处理和订单创建禁用重试。 +--- + + + + `x-fern-retries` 扩展仅在 Python SDK 中受支持。 + + +`x-fern-retries` 扩展在您的 OpenAPI 规范中为每个端点配置重试行为,覆盖 SDK 用户配置的任何重试设置。使用它可以为非幂等操作(如支付处理或订单创建)禁用重试。 + +要为特定端点禁用重试,请设置 `disabled: true`。 + +```yaml title="openapi.yml" {4-5,15-16} +paths: + /plants/{plantId}: + get: + x-fern-retries: + disabled: true + operationId: get_plant + parameters: + - name: plantId + in: path + required: true + schema: + type: string + /plants: + post: + x-fern-retries: + disabled: true + operationId: create_plant + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + species: + type: string + responses: + '201': + description: Plant created successfully +``` + +当在端点级别禁用重试时,生成的 SDK 不会重试对该端点的失败请求,无论 SDK 用户的自定义配置如何。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/schema-names.mdx b/fern/translations/zh/products/api-def/openapi/extensions/schema-names.mdx new file mode 100644 index 000000000..c3fa6f467 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/schema-names.mdx @@ -0,0 +1,84 @@ +--- +title: 模式名称 +sidebar-title: 模式名称 +headline: 模式名称 (OpenAPI) +description: 使用 `x-fern-type-name` 扩展覆盖内联模式的自动生成名称 +--- + +OpenAPI 允许您定义没有名称的内联模式。 + +```yaml title="openapi.yml 中的内联类型" {11} +components: + schemas: + Movie: + type: object + properties: + name: + type: string + cast: + type: array + items: + type: object + properties: + firstName: + type: string + lastName: + type: string + age: + type: integer +``` + +Fern 会自动为所有内联模式生成名称。例如,在这个示例中, +Fern 会为内联数组项模式生成名称 `CastItem`。 + +```typescript title="自动生成的名称" {6} +export interface Movie { + name?: string; + cast?: CastItem[]; +} + +export interface CastItem { + firstName?: string; + lastName?: string; + age?: integer; +} +``` + +如果您想覆盖生成的名称,可以使用扩展 `x-fern-type-name`。 + +```yaml title="openapi.yml" {12} +components: + schemas: + Movie: + type: object + properties: + name: + type: string + cast: + type: array + items: + type: object + x-fern-type-name: Person + properties: + firstName: + type: string + lastName: + type: string + age: + type: integer +``` + +这将用 `Person` 替换 `CastItem`,生成的代码将读起来更加自然: + +```typescript title="覆盖的名称" {6} +export interface Movie { + name?: string; + cast?: Person[]; +} + +export interface Person { + firstName?: string; + lastName?: string; + age?: integer; +} +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/sdk-variables.mdx b/fern/translations/zh/products/api-def/openapi/extensions/sdk-variables.mdx new file mode 100644 index 000000000..8c140ea09 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/sdk-variables.mdx @@ -0,0 +1,80 @@ +--- +title: SDK 变量 +headline: SDK 变量(OpenAPI) +description: 使用 `x-fern-sdk-variables` 在所有请求中设置通用路径参数 +sidebar-title: SDK 变量 +--- + +`x-fern-sdk-variables` 扩展允许您定义在 SDK 客户端初始化时设置一次的变量,并在所有端点调用的路径参数中自动使用。这对于在许多端点路径中出现的通用参数(如租户 ID、组织 ID 或环境标识符)非常有用。 + + + SDK 变量在 TypeScript (v2.6.3+)、Python (v4.24.0+) 和 Java (v3.6.3+) 中受支持。仅支持字符串类型。 + + +## 配置 + +使用 `x-fern-sdk-variables` 在文档级别定义变量,然后使用 `x-fern-sdk-variable` 将路径参数标记为变量: + +```yaml {1-7,17,23} title="openapi.yml" +x-fern-sdk-variables: + gardenId: + type: string + description: The unique identifier for your garden + zoneId: + type: string + description: The zone within the garden + +paths: + /gardens/{gardenId}/zones/{zoneId}/plants: + get: + operationId: list_plants_in_zone + parameters: + - name: gardenId + in: path + required: true + x-fern-sdk-variable: gardenId + schema: + type: string + - name: zoneId + in: path + required: true + x-fern-sdk-variable: zoneId + schema: + type: string +``` + +## SDK 使用 + +变量成为必需的构造函数参数,而不是传递给单个方法调用: + + +```typescript {2-3} +const client = new PlantClient({ + gardenId: "garden_123", + zoneId: "zone_456", + apiKey: "your-api-key" +}); + +const plants = await client.listPlantsInZone(); +``` + +```python {2-3} +client = PlantClient( + garden_id="garden_123", + zone_id="zone_456", + api_key="your-api-key" +) + +plants = client.list_plants_in_zone() +``` + +```java {2-3} +PlantClient client = PlantClient.builder() + .gardenId("garden_123") + .zoneId("zone_456") + .apiKey("your-api-key") + .build(); + +List plants = client.listPlantsInZone(); +``` + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/server-names.mdx b/fern/translations/zh/products/api-def/openapi/extensions/server-names.mdx new file mode 100644 index 000000000..03c552677 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/server-names.mdx @@ -0,0 +1,84 @@ +--- +title: 服务器名称和 URL 模板化 +headline: 服务器名称和 URL 模板化(OpenAPI) +description: 了解如何在 OpenAPI 规范中使用 x-fern-server-name、x-fern-default-url 和 URL 模板变量,以便更好地生成 SDK。 +sidebar-title: 服务器名称和 URL 模板化 +--- + +Fern 支持多个扩展来配置 OpenAPI 规范中的 `servers` 块。使用 `x-fern-server-name` 为服务器提供可读的名称,然后将其与 URL 模板变量和 `x-fern-default-url` 结合使用,以在生成的 SDK 中支持动态基础 URL。 + +## `x-fern-server-name` + +使用 `x-fern-server-name` 为每个服务器条目提供可读的名称。Fern 在生成的环境枚举中使用此名称。 + +`x-fern-server-name` 扩展用于命名您的服务器。 + +```yaml title="openapi.yml" +servers: + - url: https://api.example.com + x-fern-server-name: Production + - url: https://sandbox.example.com + x-fern-server-name: Sandbox +``` + +在生成的 TypeScript SDK 中,您会看到: + +```typescript title="environment.ts" +export const ExampleEnvironment = { + Production: "https://api.example.com", + Sandbox: "https://sandbox.example.com", +} as const; + +export type ExampleEnvironment = typeof ExampleEnvironment.Production; +``` + +### 多个基础 URL + +不同的端点可以针对不同的服务器。直接在端点上添加 `servers` 块以覆盖该路径的顶级服务器。使用 `x-fern-server-name` 为每个端点级服务器提供不同的名称,以便 Fern 能够正确分组。 + +```yaml title="openapi.yml" +paths: + /auth/token: + post: + servers: + - url: https://auth.example.com + x-fern-server-name: Auth +``` + +当 Fern 检测到多个命名服务器时,生成的 SDK 客户端会自动将每个端点解析到正确的基础 URL。 + +`x-fern-server-name` 也与 URL 模板变量一起使用,如下所示。 + +## URL 模板化和 `x-fern-default-url` + +URL 模板变量和 `x-fern-default-url` 目前仅支持 Python 和 Java SDK 生成。 + +对于跨多个区域、环境或自定义域部署的 API,您可以使用 `{variable}` 语法在服务器 `url` 中定义变量占位符。Fern 读取这些变量并将其作为生成的 SDK 中的可配置参数公开,以便用户可以在运行时自定义基础 URL。 + +由于像 `https://api.{region}.example.com/v1` 这样的模板化 URL 本身并不有效,因此使用 `x-fern-default-url` 提供具体的回退值。这样,不需要自定义变量的 SDK 用户就能获得开箱即用的工作客户端。 + +```yaml title="openapi.yml" +servers: + - url: https://api.{region}.{environment}.example.com/v1 + description: Regional API server + x-fern-server-name: Default + x-fern-default-url: https://api.example.com/v1 + variables: + region: + default: us-east-1 + enum: + - us-east-1 + - us-west-2 + - eu-west-1 + environment: + default: prod + enum: + - prod + - staging + - dev +``` + +每个变量都在 `variables` 映射下定义,并支持以下字段: + +- `default` — 当 SDK 用户未提供值时使用的回退值。此字段是 OpenAPI 规范所要求的。 +- `enum` — 允许值的可选列表。当提供时,Fern 在 SDK 中将变量生成为类型化枚举,而不是开放字符串。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/extensions/tag-display-name.mdx b/fern/translations/zh/products/api-def/openapi/extensions/tag-display-name.mdx new file mode 100644 index 000000000..92d6e7f3b --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/extensions/tag-display-name.mdx @@ -0,0 +1,48 @@ +--- +title: 标签显示名称 +headline: 标签显示名称 (OpenAPI) +description: 使用 `x-displayName` 扩展自定义标签名称在 API Reference 中的显示方式 +sidebar-title: 标签显示名称 +--- + +默认情况下,OpenAPI 规范中的标签名称会成为 API Reference 中的章节名称。使用 `x-displayName` 扩展来控制这些章节名称在生成的文档中的显示方式。这有助于控制大小写和创建用户友好的名称。 + +在 OpenAPI 规范的根级别定义带有显示名称的标签,然后在操作中按名称引用它们: + +```yaml title="openapi.yml" +openapi: 3.0.0 +info: + title: Plant Care API + version: 1.0.0 + +tags: + - name: plant + x-displayName: Plants + description: Operations for managing plants + - name: watering-schedule + x-displayName: Watering Schedules + description: Handle watering schedule operations + +paths: + /plants: + get: + tags: + - plant + summary: List all plants + /plants/{plantId}: + get: + tags: + - plant + summary: Get a plant by ID + /watering-schedules: + get: + tags: + - watering-schedule + summary: List watering schedules +``` + +您还可以在[覆盖文件](/learn/api-definitions/openapi/overrides)中设置显示名称。 + + + 或者,您也可以直接在[`docs.yml` 文件](/learn/docs/api-references/customize-api-reference-layout)中重命名章节。 + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/overlays.mdx b/fern/translations/zh/products/api-def/openapi/overlays.mdx new file mode 100644 index 000000000..4bcae0386 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/overlays.mdx @@ -0,0 +1,195 @@ +--- +title: Overlays +description: 使用 OpenAPI Overlay 规范来自定义您的 OpenAPI 定义,而无需修改原始规范。 +sidebar-title: Overlays +--- + +Overlays 让您可以自定义 OpenAPI 规范而无需修改原始文件。这在以下情况下很有用: + +- 您的 API 规范是从服务器代码自动生成的 +- 您需要为 SDK 和文档配置不同的设置 +- 您想要添加 Fern 配置,如分页或 SDK 方法名称 +- 您想要使用 JSONPath 通配符对多个端点进行批量更改 + +Overlays 遵循 [OpenAPI Overlay 规范](https://spec.openapis.org/overlay/v1.0.0.html),并且在 OpenAPI 生态系统中是可移植的。 + + + Fern 建议对 OpenAPI 规范使用 overlays 而不是 overrides。 + + [Overrides](/learn/api-definitions/openapi/overrides) 也完全受支持。如果 overrides 对您的团队有效,则无需切换。您也可以同时使用两者(先应用 overrides,然后应用 overlays)。 + + +## 配置 overlays + +要使用 overlays,请在与您的规范相同的文件夹中创建一个 `overlays` 文件,并在 `generators.yml` 中引用它: + +```yaml title="generators.yml" +api: + specs: + - openapi: openapi.json + overlays: overlays.yml +``` + +## 定义 overlays 文件 + +overlay 中的每个动作使用 [JSONPath](https://datatracker.ietf.org/doc/html/rfc9535) 定位元素,并应用 `update` 或 `remove` 操作: + +```yaml title="openapi-overlays.yml" +overlay: 1.0.0 # 必需:Overlay 规范版本 +info: + title: 自定义植物商店 API # 必需:overlay 目的的可读描述 + version: 1.0.0 # 必需:用于跟踪此 overlay 更改的版本标识符 +actions: # 必需:要应用的更改的有序列表 + - target: $.info # 选择要修改的元素的 JSONPath 表达式 + update: # 要与目标元素合并的属性 + x-fern-sdk-group-name: plants + - target: $.paths['/plants/{plantId}'].get.parameters[?(@.name == 'plantId')] + update: + x-fern-parameter-name: id +``` + + +Fern 要求在 JSONPath 筛选表达式周围使用括号。请使用 `[?(@.name == 'plantId')]` 而不是 `[?@.name == 'plantId']`。 + + + + + +使用 `update` 来更改标准 OpenAPI 属性,如描述、摘要或其他字段: + +```yaml title="openapi-overlays.yml" +overlay: 1.0.0 +info: + title: 改进 API 文档 + version: 1.0.0 +actions: + - target: $.paths['/plants'].get + update: + summary: 列出所有可用植物 + description: 返回商店库存中植物的分页列表。 +``` + + + + +使用 `update` 添加 [Fern 扩展](/api-definitions/openapi/extensions/overview): + +```yaml title="openapi-overlays.yml" +overlay: 1.0.0 +info: + title: 添加 Fern SDK 自定义 + version: 1.0.0 +actions: + # 添加 SDK 组和方法名称 + - target: $.paths['/plants'].get + update: + x-fern-sdk-group-name: plants + x-fern-sdk-method-name: list + - target: $.paths['/plants'].post + update: + x-fern-sdk-group-name: plants + x-fern-sdk-method-name: create + # 重命名参数 + - target: $.paths['/plants/{plantId}'].get.parameters[?(@.name == 'includeDetails')] + update: + x-fern-parameter-name: withDetails +``` + + + + +使用 `remove: true` 从您的规范中删除元素: + +```yaml title="openapi-overlays.yml" {8} +overlay: 1.0.0 +info: + title: 移除内部端点 + version: 1.0.0 +actions: + - target: $.paths['/internal/debug'] + description: 从公共 SDK 中移除调试端点 + remove: true +``` + + + + +## 跨 API 管理 overlays + +`generators.yml` 中的每个规范接受一个 overlay 文件。您可以在规范中引用相同的文件,或为不同的配置使用单独的文件。 + + + + +当多个规范需要相同的自定义时,将它们指向同一个 overlay 文件以避免重复: + +```yaml title="generators.yml" +api: + specs: + - openapi: ./payments-api.yml + overlays: shared-overlays.yml # 两个规范使用相同的 overlay + - openapi: ./users-api.yml + overlays: shared-overlays.yml +``` + +如果每个规范需要唯一的自定义,请为每个规范创建单独的 overlay 文件: + +```yaml title="generators.yml" +api: + specs: + - openapi: ./payments-api.yml + overlays: payments-overlays.yml + - openapi: ./users-api.yml + overlays: users-overlays.yml +``` + + + + +通过创建具有自己的 `generators.yml` 的单独文件夹,为 SDK 生成和文档使用不同的 overlay 文件: + + + + + + + + + + + + + + + + +```yaml title="sdks/generators.yml" +api: + specs: + - openapi: ../openapi.yml + overlays: sdk-overlays.yml +``` + + + + +为生产环境与内部 API 配置不同的 overlays: + +```yaml title="generators.yml" {5, 11} +groups: + production: + specs: + - openapi: openapi.yml + overlays: production-overlays.yml + generators: + ... + internal: + specs: + - openapi: openapi.yml + overlays: internal-overlays.yml + generators: + ... +``` + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/overview.mdx b/fern/translations/zh/products/api-def/openapi/overview.mdx new file mode 100644 index 000000000..79d8c00ff --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/overview.mdx @@ -0,0 +1,167 @@ +--- +title: 什么是 OpenAPI 规范? +description: OpenAPI 是用于记录 REST API 的标准 +sidebar-title: 什么是 OpenAPI 规范? +--- + +OpenAPI 规范 (OAS) 是开发人员用来记录 REST API 的框架。该规范以 JSON 或 YAML 格式编写,包含所有端点、参数、模式和身份验证方案。Fern 兼容最新的 OAS 版本,目前是 [v3.1.1](https://spec.openapis.org/#openapi-specification)。 + +以下是 OpenAPI 文件的示例: + +```yaml title="openapi.yml" maxLines=10 +openapi: 3.0.2 +info: + title: Petstore - OpenAPI 3.0 + description: |- + This is a sample Pet Store Server based on the OpenAPI 3.0 specification. +paths: + "/pet": + put: + tags: + - pet + summary: Update an existing pet + description: Update an existing pet by Id + operationId: updatePet + requestBody: + description: Update an existent pet in the store + content: + application/json: + schema: + "$ref": "#/components/schemas/Pet" + required: true + responses: + '200': + description: Successful operation + content: + application/json: + schema: + "$ref": "#/components/schemas/Pet" + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - api_key +components: + schemas: + Category: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + type: string + example: Dogs + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + Pet: + required: + - name + - photoUrls + type: object + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + "$ref": "#/components/schemas/Category" + photoUrls: + type: array + items: + type: string + tags: + type: array + items: + "$ref": "#/components/schemas/Tag" + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + securitySchemes: + api_key: + type: apiKey + name: api_key + in: header +``` +## 最佳实践 + +遵循这些最佳实践,确保您的 OpenAPI 规范生成高质量的 SDK 和文档: + +- **使用合适的项目结构进行组织。** 按照[项目结构](/api-definitions/overview/project-structure)中的说明,清晰地组织包含您的定义和其他相关文件的目录。 +- **为端点添加 `operationId`。** 为每个端点包含清晰的 `operationId`,以控制在 SDK 中生成的函数名称。(或使用[扩展来自定义组和方法名称](/api-definitions/openapi/extensions/method-names)。) +- **引用模式而不是内联。** 在 `components/schemas` 部分定义可重用的模式,并使用 `$ref` 引用它们。这促进了一致性,减少了重复,并使维护更容易。 + + ```yaml title="openapi.yml" {8, 14, 17-25} + paths: + /pets: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' # Clean reference + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' # Reused easily + components: + schemas: + Pet: # Defined once, used everywhere + type: object + properties: + name: + type: string + status: + type: string + enum: [available, pending, sold] + ``` + +- **使用覆盖层和 Fern 扩展进行自定义。** 使用 Fern [扩展](/api-definitions/openapi/extensions/overview)来自定义您的规范,这些扩展保存在[覆盖层文件](/api-definitions/openapi/overlays)中。这使您能够修改生成行为,而不更改核心 OpenAPI 定义。 + +一旦您的 OpenAPI 规范遵循这些实践,您就可以设置 fern 文件夹了。 + +## 设置您的 fern 文件夹 + + 正在考虑生成 OpenAPI 规范的选项?在[这里](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)获得实时支持 + +首先使用 OpenAPI 规范初始化您的 fern 文件夹 + + +```sh file +fern init --openapi ./path/to/openapi +``` +```sh url +fern init --openapi https://host/path/to/openapi +``` + + +这将初始化如下所示的目录: + + + + + + + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/reference/generators-reference.mdx b/fern/translations/zh/products/api-def/openapi/reference/generators-reference.mdx new file mode 100644 index 000000000..a69d2c11f --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/reference/generators-reference.mdx @@ -0,0 +1,11 @@ +--- +title: OpenAPI generators.yml 参考 +description: 在 generators.yml 文件中配置 OpenAPI 规范的参考指南 +sidebar-title: OpenAPI generators.yml 参考 +--- + +`generators.yml` 文件有两个作用:它在 `api.specs` 部分声明你的 OpenAPI 规范位置,并在可选的 `groups` 部分配置 SDK 生成。 + +这里声明的 API 可以通过 `docs.yml` 在你的文档中渲染。请参阅[生成你的 API 参考文档](/learn/docs/api-references/generate-api-ref)。 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/server-frameworks/fastapi.mdx b/fern/translations/zh/products/api-def/openapi/server-frameworks/fastapi.mdx new file mode 100644 index 000000000..81e381eb4 --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/server-frameworks/fastapi.mdx @@ -0,0 +1,94 @@ +--- +title: FastAPI Instrumentation +description: 了解在对 FastAPI 应用程序进行工具化时创建丰富 OpenAPI 规范的最佳实践。 +sidebar-title: FastAPI Instrumentation +--- + + +[FastAPI](https://fastapi.tiangolo.com/) 是由 [tiangolo](https://github.com/tiangolo) 开发的一个流行的 Python Web 框架。 + +该产品的品牌定位是 + +> FastAPI 是一个现代、快速(高性能)的 Web 框架,用于基于标准 Python 类型提示构建 API。 + +FastAPI 与 Fern 配合得非常好,因为它具有输出 OpenAPI 规范的能力!下面我们将概述一些使用 FastAPI 生成丰富 OpenAPI 的技巧。 + + +## OpenAPI 生成 + +默认情况下,FastAPI 将根据您的路由和数据模型为您生成 OpenAPI 规范!您可以通过访问 FastAPI 服务器上的 `/docs` 来查看此规范。 + +如果您没有看到任何 OpenAPI 规范(或 Swagger UI),您可能需要检查您的 FastAPI 服务器配置,因为路径可能已被更改或完全省略。 + +```python {6-8} +from fastapi import FastAPI + +... + +FastAPI( + openapi_url="/openapi.json", # <-- 这是访问 OpenAPI 规范所需的文件和 URL,`docs_url` 和 `redoc_url` 是在 UI 中显示文件的便捷包装器! + docs_url="/docs", # <-- 这是访问 Swagger UI 的 URL,它将指向您的 OpenAPI 规范 + redoc_url="/redoc" # <-- 这是访问 ReDoc UI 的 URL,它将指向您的 OpenAPI 规范 +) +``` + +## 指定服务器 + +Fern 将自动生成指向您在 OpenAPI 规范中配置的服务器的客户端,因此指定您的 API 将托管的服务器很重要。 + +```python {5} +from fastapi import FastAPI + +... + +app = FastAPI(servers=[{"url": "http://prod.test.com", "description": "Production server"}]) +# 这在您的 OpenAPI 规范中创建了以下服务器对象: +# "servers":[{"url":"http://prod.test.com","description":"Production server"}], +``` + +## OpenAPI 扩展 + +FastAPI 允许您通过使用 `openapi_extra` 参数直接在路由中添加额外的 OpenAPI 配置。 +下面,我们注释了 FastAPI 中的一个"良好"路由,它具有类型标注以及 Fern 扩展来帮助命名。 + +```python {5-9} +@router.post( + "/your/post/endpoint", + response_model=YourResponseModel, # <-- 在 FastAPI 中,指定响应模型很重要,这样它就会出现在 OpenAPI 规范中 + summary="Get some response for your req", # <-- 如果您想为端点添加描述,可以在这里添加 + openapi_extra={ # <-- 最后,您可以在这里添加 Fern 扩展,这些扩展将产生类似于 python 中 `client.endpoints.create(...)` 的 SDK 代码 + "x-fern-sdk-method-name": "create", + "x-fern-sdk-group-name": "endpoints", + "x-fern-availability": "beta", + }, +) +async def your_post_endpoint( + payload: YourRequestModel, +) -> YourResponseModel: +``` + +## 指定示例 + +FastAPI 允许您为数据模型指定示例,Fern 将自动获取并在生成的 SDK 和文档中使用这些示例。 + +有关在 Fern 中利用示例的更多信息,请参阅 [Fern 文档](/learn/api-definitions/openapi/extensions/request-response-examples)。 + +有关此 FastAPI 功能的更多信息,请参阅 [FastAPI 文档](https://fastapi.tiangolo.com/tutorial/schema-extra-example/)。 + +```python {7-11} +from pydantic import BaseModel + +class MyObject(BaseModel): + id: str + + class Config: + schema_extra = { + "example": { + "id": "a-cool-uuid", + } + } +``` + +## 其他自定义 + +FastAPI 在如何自定义 OpenAPI 规范方面具有很大的灵活性。更多信息请参阅 [FastAPI 文档](https://fastapi.tiangolo.com/how-to/extending-openapi/#modify-the-openapi-schema)。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/servers.mdx b/fern/translations/zh/products/api-def/openapi/servers.mdx new file mode 100644 index 000000000..f68d4420b --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/servers.mdx @@ -0,0 +1,80 @@ +--- +title: 服务器 +description: 配置服务器 URL 和环境以帮助用户连接到您的 API。 +subtitle: 定义服务器 URL 和环境以帮助用户连接到您的 API。 +sidebar-title: 服务器 +--- + + +OpenAPI 允许您在 `servers` 键下指定一个或多个基础 URL。 + +```yml openapi.yml + +servers: + - url: https://api.yourcompany.com/ + - url: https://api.eu.yourcompany.com/ +``` + +指定服务器对 SDK 和文档都很有价值: +- 对于 SDK,您的用户无需在客户端实例化时手动指定 baseURL +- 对于文档,您的 API 调试工具将自动连接到正确的服务器 + +## 命名您的服务器 + +如果您有多个服务器,我们建议指定 `x-fern-server-name` 来为服务器命名。 + +```yml openapi.yml {3,5} +servers: + - x-fern-server-name: Production + url: https://api.yourcompany.com/ + - x-fern-server-name: Production_EU + url: https://api.eu.yourcompany.com/ +``` + +## 单个 API 的多个基础 URL + +如果您采用微服务架构,可能会有不同的端点托管在不同的 URL 上。例如,您的 AI 端点可能托管在 `ai.yourcompany.com`,而其余端点可能托管在 `api.yourcompany.com`。 + +要指定这种情况,您需要在 `generators.yml` 和 OpenAPI 规范中都添加配置。下面的代码片段显示了如何在 `generators.yml` 中配置具有多个 URL 的环境。 + +```yml generators.yml {3-8} +api: + default-environment: Production + default-url: api + environments: + Production: + api: api.yourcompany.com + ai: ai.yourcompany.com + specs: + - openapi: ./path/to/your/openapi + overrides: ./path/to/your/overrides # optional +``` + +一旦您在 `generators.yml` 中指定了环境,就可以使用 `x-fern-server-name` 扩展来指定操作所属的服务器。 + +```yml openapi.yml {4} +paths: + /chat: + post: + x-fern-server-name: ai +``` + +如果您有多个环境,如开发或测试环境,也可以在 `generators.yml` 中建模。 + +```yml generators.yml {7-12} +api: + default-environment: Production + default-url: api + environments: + Production: + api: api.yourcompany.com + ai: ai.yourcompany.com + Staging: + api: api.staging.yourcompany.com + ai: ai.staging.yourcompany.com + Dev: + api: api.dev.yourcompany.com + ai: ai.dev.yourcompany.com +``` + +要查看生产环境中的示例,请查看 Chariot [generators.yml](https://github.com/chariot-giving/chariot-openapi/blob/main/fern/apis/2025-02-24/generators.yml) \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openapi/webhooks.mdx b/fern/translations/zh/products/api-def/openapi/webhooks.mdx new file mode 100644 index 000000000..1fafe9ecb --- /dev/null +++ b/fern/translations/zh/products/api-def/openapi/webhooks.mdx @@ -0,0 +1,188 @@ +--- +title: Webhooks +description: 了解如何使用 OpenAPI 3.1 原生支持或 Fern 的扩展定义 webhooks。生成 webhook 文档和 SDK 验证工具。 +sidebar-title: Webhooks +max-toc-depth: 3 +--- + + +Fern 支持两种在 OpenAPI 规范中定义 webhooks 的方法: + +1. 使用 OpenAPI 3.1 的原生 webhook 支持(推荐) +2. 使用 Fern 的 `x-fern-webhook` 扩展 + +## OpenAPI 3.1 webhooks + +对于 OpenAPI 3.1 规范,使用 `webhooks` 顶级字段来定义您的 webhook 操作。每个 webhook 都需要一个 `operationId` 才能被 Fern 正确处理。 + +```yaml openapi.yml {4, 7-8, 42-48} +webhooks: + newPlant: + post: + operationId: newPlantWebhook # Defines webhook + summary: New Plant Added + description: Information about a new plant that was added to the store + tags: + - Plants # Creates dedicated page + requestBody: + description: The plant data when a new plant is added + content: + application/json: + schema: + description: The Webhook payload for when a new plant is added to the store + properties: + triggerType: + description: The type of event that triggered the request + type: string + example: "new_plant" + payload: + type: object + description: The payload of data sent from the plant store + properties: + plantId: + type: string + description: The unique identifier for the plant + example: "64f1a2b3c5d6e7f8a9b0c1d2" + name: + type: string + description: The name of the plant + example: "Monstera Deliciosa" + price: + type: number + format: float + description: The price of the plant in dollars + example: 29.99 + addedAt: + type: string + format: date-time + description: The timestamp when the plant was added + example: "2024-01-15T10:30:00.000Z" + example: # Full payload example for docs + triggerType: "new_plant" + payload: + plantId: "64f1a2b3c5d6e7f8a9b0c1d2" + name: "Monstera Deliciosa" + price: 29.99 + addedAt: "2024-01-15T10:30:00.000Z" + responses: + '200': + description: Return a 200 status to indicate that the data was received successfully +``` + +## Fern webhook 扩展 + +对于 OpenAPI 3.0,使用 `x-fern-webhook: true` 扩展来定义 webhooks。Fern 会将 `requestBody` 视为 webhook 负载。 + +```yaml openapi.yml {6-8, 23-25} +paths: + /payment/updated/: + post: + summary: Payment Initiated + operationId: initiatePayment + tags: + - Payments # Creates dedicated page + x-fern-webhook: true # Defines webhooks + requestBody: + content: + application/json: + schema: + type: object + properties: + amount: + type: number + example: 99.99 + currency: + $ref: '#/components/schemas/Currency' + required: + - amount + - currency + example: # Full payload example for docs + amount: 99.99 + currency: "USD" +``` + + +定义 webhook 时选择的路径可以是任意的。由于 webhooks 可以发送到任何服务器,Fern 会忽略路径。 + + +## 生成 webhook 参考 + +Fern Docs 可以从您的定义中自动生成 webhook 参考文档。在您的 `docs.yml` 文件中进行设置。 + +您的 webhook 参考可以是单个文档页面: + +```yml docs.yml +navigation: + - api: Webhook Reference # Display name for this page + api-name: webhooks-v1 # Name of webhook definition directory +``` + +或者您可以为每个 webhook 事件配置单独的文档页面: + +```yaml title="docs.yml" +navigation: + - subpackage_plants.newPlantWebhook # subpackage_{tag}.{webhook-event-name} +``` + +有关如何在 `docs.yml` 中配置 webhook 参考的更多信息,请参阅[生成 webhook 参考](/docs/api-references/generate-webhook-ref)。 + +## SDK 签名验证 + +使用 `x-fern-webhook-signature` 扩展来配置 webhook 签名验证。配置后,Fern 会生成 SDK 工具,允许用户验证 webhook 签名并确保事件来自您的 API。 + +配置可以在**文档级别**(适用于所有 webhook 端点)或**每个端点**(覆盖文档级别的默认设置)设置。两个级别都接受相同的配置选项。 + +Fern 支持两种验证方法:**HMAC**(使用共享密钥的对称密钥验证)和**非对称**(使用 RSA、ECDSA 或 Ed25519 密钥的公钥验证)。 + + + +```yaml title="openapi.yml" +x-fern-webhook-signature: + type: hmac + header: x-webhook-signature + algorithm: sha256 + encoding: hex + +paths: + /webhooks/payment: + post: + x-fern-webhook: true + # Inherits document-level signature config + + /webhooks/order: + post: + x-fern-webhook: true + # Inherits document-level signature config +``` + + +```yaml title="openapi.yml" +x-fern-webhook-signature: + type: hmac + header: x-webhook-signature + algorithm: sha256 + encoding: hex + +paths: + /webhooks/payment: + post: + x-fern-webhook: true + # Inherits document-level config + + /webhooks/refund: + post: + x-fern-webhook: true + x-fern-webhook-signature: + # Override with asymmetric + type: asymmetric + header: x-refund-signature + asymmetric-algorithm: rsa-sha256 + encoding: base64 + jwks-url: https://api.example.com/.well-known/jwks.json +``` + + + +### 配置选项 + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/auth.mdx b/fern/translations/zh/products/api-def/openrpc/auth.mdx new file mode 100644 index 000000000..da2ccfb2d --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/auth.mdx @@ -0,0 +1,236 @@ +--- +title: Authentication +subtitle: 为 JSON-RPC API 建模身份验证方案,包括 Bearer、Basic 和 API Key 身份验证 +sidebar-title: 身份验证 +--- + +OpenRPC 中的身份验证可以在服务器级别或方法级别进行配置,具体取决于您的 JSON-RPC 实现。与 REST API 不同,JSON-RPC 通常通过传输层(HTTP 头)或在 JSON-RPC 请求负载中处理身份验证。 + +## HTTP 传输身份验证 + +当使用 HTTP 作为 JSON-RPC 的传输方式时,您可以使用标准的 HTTP 身份验证方案。 + +### Bearer token 身份验证 + +为基于 HTTP 的 JSON-RPC 配置 bearer token 身份验证: + +```yml title="openrpc.yml" {4-9} +servers: + - name: production + url: https://api.example.com/rpc + description: Production JSON-RPC server + security: + - bearerAuth: [] +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT +``` + +这会生成需要 token 的 SDK 方法: + +```typescript +const client = new JSONRPCClient({ + url: "https://api.example.com/rpc", + auth: { + bearer: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } +}); + +// 调用 JSON-RPC 方法 +const result = await client.call("calculate.add", { a: 5, b: 3 }); +``` + +### API Key 身份验证 + +配置 API Key 身份验证: + +```yml title="openrpc.yml" {4-9} +servers: + - name: production + url: https://api.example.com/rpc + description: Production JSON-RPC server + security: + - apiKeyAuth: [] +components: + securitySchemes: + apiKeyAuth: + type: apiKey + in: header + name: X-API-Key +``` + +在 SDK 中的使用方式: + +```typescript +const client = new JSONRPCClient({ + url: "https://api.example.com/rpc", + auth: { + apiKey: "your-api-key-here" + } +}); +``` + +### Basic 身份验证 + +配置 Basic 身份验证: + +```yml title="openrpc.yml" {4-9} +servers: + - name: production + url: https://api.example.com/rpc + description: Production JSON-RPC server + security: + - basicAuth: [] +components: + securitySchemes: + basicAuth: + type: http + scheme: basic +``` + +在 SDK 中的使用方式: + +```typescript +const client = new JSONRPCClient({ + url: "https://api.example.com/rpc", + auth: { + username: "user@example.com", + password: "password123" + } +}); +``` + +## 方法级别的身份验证 + +一些 JSON-RPC 实现可能需要为特定方法使用不同的身份验证: + +```yml title="openrpc.yml" {6-7, 15-16} +methods: + - name: public.getInfo + summary: Get public information + description: Publicly accessible method (no auth required) + params: [] + result: + name: info + schema: + type: object + - name: user.getProfile + summary: Get user profile + description: Requires user authentication + security: + - bearerAuth: [] + params: + - name: userId + schema: + type: string + required: true + result: + name: profile + schema: + $ref: '#/components/schemas/UserProfile' +``` + +## WebSocket 身份验证 + +对于 WebSocket 传输,身份验证通常在连接建立过程中进行: + +```yml title="openrpc.yml" {4-8} +servers: + - name: websocket + url: wss://api.example.com/rpc + description: WebSocket JSON-RPC server + variables: + token: + description: Authentication token for WebSocket connection + default: "" + security: + - wsAuth: [] +components: + securitySchemes: + wsAuth: + type: apiKey + in: query + name: token + description: Authentication token passed as query parameter +``` + +## 自定义身份验证参数 + +对于在请求负载内处理身份验证的 JSON-RPC API: + +```yml title="openrpc.yml" {8-16} +methods: + - name: auth.login + summary: Authenticate user + description: Login method that returns authentication token + params: + - name: credentials + schema: + type: object + properties: + username: + type: string + password: + type: string + required: + - username + - password + result: + name: authResult + schema: + type: object + properties: + token: + type: string + expiresIn: + type: integer + refreshToken: + type: string +``` + +## Fern 身份验证扩展 + +使用 Fern 扩展来自定义身份验证行为: + +```yml title="openrpc.yml" {5-8} +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + x-fern-token: + name: authToken + env: AUTH_TOKEN +``` + +这允许用户通过环境变量或构造函数参数设置身份验证,使 SDK 更加灵活和安全。 + +## 身份验证的错误处理 + +为身份验证失败定义标准化的错误响应: + +```yml title="openrpc.yml" {2-12} +components: + errors: + - code: -32001 + message: Authentication required + data: + type: object + properties: + error: + type: string + const: "Authentication token is required" + - code: -32002 + message: Invalid authentication + data: + type: object + properties: + error: + type: string + const: "Invalid or expired authentication token" +``` + +这些错误代码遵循 JSON-RPC 2.0 约定,同时为 API 消费者提供清晰的身份验证反馈。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/automation.mdx b/fern/translations/zh/products/api-def/openrpc/automation.mdx new file mode 100644 index 000000000..0ee2204d6 --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/automation.mdx @@ -0,0 +1,317 @@ +--- +title: 同步 OpenRPC 规范 +subtitle: 自动化 OpenRPC 规范同步,保持 SDK 和文档的最新状态。使用 Fern 设置 GitHub Actions、webhook 和 CI/CD 集成。 +sidebar-title: 同步 OpenRPC 规范 +--- + +保持 OpenRPC 规范与代码库同步对于维护准确的 SDK 和文档至关重要。Fern 提供多种自动化选项来简化这一过程。 + +## GitHub Actions + +使用 Fern 的 GitHub Action 在 OpenRPC 规范发生变化时自动更新 SDK 和文档: + +```yaml title=".github/workflows/fern.yml" +name: Fern + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + fern-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Check OpenRPC spec + uses: fern-api/action@v0 + with: + command: check + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + + fern-generate: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Generate SDKs and docs + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## Webhook 集成 + +设置 webhook 在 OpenRPC 规范更新时触发 SDK 生成: + +```yaml title="generators.yml" {4-8} +api: + specs: + - spec: openrpc.yml + github: + repository: your-org/your-repo + webhooks: + - url: https://your-api.com/webhooks/fern + events: [generate] + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@your-org/jsonrpc-sdk" +``` + +## 从源自动同步 + +配置 Fern 自动从各种源拉取 OpenRPC 规范: + +### 从 URL +```yaml title="generators.yml" {3-4} +api: + specs: + - spec: https://api.yourcompany.com/openrpc.yml + auto-sync: true + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +### 从 git 仓库 +```yaml title="generators.yml" {3-7} +api: + specs: + - spec: + git: + repository: https://github.com/your-org/api-specs + path: openrpc/api.yml + branch: main + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +## CI/CD 集成 + +### CircleCI +```yaml title=".circleci/config.yml" {15-23} +version: 2.1 + +orbs: + fern: fernapi/fern@1.0 + +workflows: + version: 2 + build-and-test: + jobs: + - build + - test: + requires: + - build + - fern/generate: + requires: + - test + filters: + branches: + only: main + context: + - fern-context +``` + +### GitLab CI +```yaml title=".gitlab-ci.yml" {13-20} +stages: + - build + - test + - generate + +variables: + FERN_TOKEN: $FERN_TOKEN + +build: + stage: build + script: + - echo "Building JSON-RPC service..." + +generate-sdks: + stage: generate + image: fernapi/fern:latest + script: + - fern generate + only: + - main +``` + +## 定期更新 + +设置定期更新以确保 SDK 保持最新状态: + +```yaml title=".github/workflows/scheduled-update.yml" +name: Scheduled OpenRPC Update + +on: + schedule: + - cron: '0 2 * * 1' # Every Monday at 2 AM UTC + workflow_dispatch: + +jobs: + update-specs: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Update OpenRPC specs + run: | + curl -o fern/openrpc/openrpc.yml https://api.yourcompany.com/openrpc.yml + + - name: Generate with latest spec + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## 从 JSON-RPC 服务器生成代码 + +对于能够生成自己的 OpenRPC 规范的服务器: + +```yaml title=".github/workflows/auto-generate.yml" +name: Auto-generate from JSON-RPC server + +on: + push: + paths: + - 'src/**/*.py' # Trigger on server code changes + - 'src/**/*.js' + - 'src/**/*.ts' + +jobs: + generate-spec: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup environment + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install dependencies + run: | + pip install -r requirements.txt + + - name: Generate OpenRPC spec + run: | + python scripts/generate_openrpc.py --output fern/openrpc/openrpc.yml + + - name: Generate SDKs + uses: fern-api/action@v0 + with: + command: generate + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} +``` + +## 监控变更 + +跟踪 OpenRPC 规范的变更: + +```yaml title="generators.yml" {4-9} +api: + specs: + - spec: openrpc.yml + change-detection: + enabled: true + breaking-changes: error + notifications: + slack: ${{ secrets.SLACK_WEBHOOK }} + email: team@yourcompany.com + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + +## 多环境同步 + +为不同环境同步不同的规范: + +```yaml title="generators.yml" {3-7, 12-16, 21-25} +environments: + production: + specs: + - spec: https://api.prod.yourcompany.com/openrpc.yml + overlays: + - prod-overlay.yml + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@yourcompany/prod-sdk" + staging: + specs: + - spec: https://api.staging.yourcompany.com/openrpc.yml + overlays: + - staging-overlay.yml + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: npm + package-name: "@yourcompany/staging-sdk" + development: + specs: + - spec: http://localhost:8080/openrpc.yml + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 + output: + location: local + path: ./generated-sdk +``` + +## JSON-RPC 服务器内省 + +对于支持 OpenRPC 发现的服务器: + +```python title="scripts/sync_from_server.py" +import requests +import yaml +import json + +def fetch_openrpc_spec(server_url): + """Fetch OpenRPC spec from a JSON-RPC server that supports introspection""" + payload = { + "jsonrpc": "2.0", + "method": "rpc.discover", + "id": 1 + } + + response = requests.post(f"{server_url}/rpc", json=payload) + spec = response.json()["result"] + + return spec + +def save_spec_as_yaml(spec, output_path): + """Convert JSON spec to YAML and save""" + with open(output_path, 'w') as f: + yaml.dump(spec, f, default_flow_style=False) + +if __name__ == "__main__": + spec = fetch_openrpc_spec("https://api.yourcompany.com") + save_spec_as_yaml(spec, "fern/openrpc/openrpc.yml") +``` + +这确保了对 OpenRPC 规范的任何破坏性更改都能被检测到,并且在更改传播到 SDK 和文档之前,相关团队成员会收到通知。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/audiences.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/audiences.mdx new file mode 100644 index 000000000..97333977a --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/audiences.mdx @@ -0,0 +1,297 @@ +--- +title: 使用受众过滤您的 API +headline: 使用受众过滤您的 API (OpenRPC) +subtitle: 使用 `x-fern-audiences` 过滤相关方法、参数和模式 +sidebar-title: 使用受众过滤 API +--- + +受众是一个有用的工具,用于为不同的消费者分割您的 JSON-RPC API。常见的受众示例包括 `public` +和 `beta`。 + + + 记住在指定受众后过滤您的 SDK 和文档。如果**未指定受众**, + 将不会过滤任何内容。 + + + + +以下示例配置 SDK 过滤到 `public` 受众: + +```yaml title="generators.yml" {3-4} +groups: + sdks: + audiences: + - public + generators: + - name: fernapi/fern-typescript-sdk + version: 0.8.8 +``` + + + +以下示例配置文档过滤到 `public` 受众: + +```yaml title="docs.yml" {3-4} +navigation: + - api: API Reference + audiences: + - public +``` + + + + + +## 过滤方法 + +向方法添加 `x-fern-audiences` 来控制特定受众包含哪些方法: + +```yaml title="openrpc.yml" {5-6, 12-13} +methods: + - name: public.getInfo + summary: Get public API information + description: Publicly available information about the API + x-fern-audiences: + - public + params: [] + result: + name: info + schema: + $ref: '#/components/schemas/ApiInfo' + - name: admin.getUsers + summary: Get all users (admin only) + x-fern-audiences: + - admin + params: + - name: limit + schema: + type: integer + default: 100 + result: + name: users + schema: + type: array + items: + $ref: '#/components/schemas/User' +``` + +## 过滤方法参数 + +您可以过滤方法内的特定参数: + +```yaml title="openrpc.yml" {8-9, 14-15} +methods: + - name: user.create + summary: Create a new user + params: + - name: userData + schema: + type: object + properties: + email: + type: string + format: email + name: + type: string + adminNotes: + type: string + x-fern-audiences: + - admin + internalId: + type: string + x-fern-audiences: + - internal + required: + - email + - name + required: true + result: + name: user + schema: + $ref: '#/components/schemas/User' +``` + +## 过滤模式 + +将整个模式过滤到不同的受众: + +```yaml title="openrpc.yml" {5-6, 20-21} +components: + schemas: + PublicUser: + type: object + x-fern-audiences: + - public + properties: + id: + type: string + name: + type: string + email: + type: string + required: + - id + - name + - email + AdminUser: + allOf: + - $ref: '#/components/schemas/PublicUser' + - type: object + x-fern-audiences: + - admin + properties: + role: + type: string + permissions: + type: array + items: + type: string + createdAt: + type: string + format: date-time + lastLoginAt: + type: string + format: date-time +``` + +## 过滤模式属性 + +您可以过滤模式内的各个属性: + +```yaml title="openrpc.yml" {9-10, 13-14, 17-18} +components: + schemas: + Order: + type: object + properties: + id: + type: string + amount: + type: number + x-fern-audiences: + - public + internalCost: + type: number + x-fern-audiences: + - internal + debugInfo: + type: object + x-fern-audiences: + - debug + customerInfo: + type: object + properties: + id: + type: string + email: + type: string + x-fern-audiences: + - admin +``` + +## 过滤错误响应 + +根据受众过滤错误信息: + +```yaml title="openrpc.yml" {7-12, 14-19} +methods: + - name: payment.process + summary: Process a payment + params: + - name: paymentData + schema: + $ref: '#/components/schemas/PaymentRequest' + errors: + - code: -32001 + message: Payment failed + x-fern-audiences: + - public + data: + type: object + properties: + error: + type: string + const: "Payment could not be processed" + - code: -32001 + message: Payment failed + x-fern-audiences: + - admin + data: + type: object + properties: + error: + type: string + errorCode: + type: string + gatewayResponse: + type: object + debugTrace: + type: string +``` + +## 服务器级别过滤 + +在服务器级别应用受众过滤: + +```yaml title="openrpc.yml" {4-5, 10-11} +servers: + - name: public-api + url: https://api.example.com/rpc + x-fern-audiences: + - public + description: Public API server + - name: admin-api + url: https://admin-api.example.com/rpc + x-fern-audiences: + - admin + description: Admin API server with additional privileges +``` + +## 条件方法可用性 + +使用受众使方法仅在特定上下文中可用: + +```yaml title="openrpc.yml" {5-6, 15-16, 25-26} +methods: + - name: debug.getSystemInfo + summary: Get system debug information + description: Internal system information for debugging + x-fern-audiences: + - debug + params: [] + result: + name: systemInfo + schema: + type: object + additionalProperties: true + - name: beta.advancedSearch + summary: Advanced search functionality + x-fern-audiences: + - beta + params: + - name: query + schema: + type: object + properties: + text: + type: string + filters: + type: object + additionalProperties: true + result: + name: results + schema: + $ref: '#/components/schemas/SearchResults' + - name: internal.resetCache + summary: Reset internal caches + x-fern-audiences: + - internal + params: + - name: cacheType + schema: + type: string + enum: [user, product, session, all] + # Notification - no result expected +``` + +这允许您为不同类型的消费者创建同一个 JSON-RPC API 的不同视图,确保每个受众只看到与其用例相关的方法和数据。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/availability.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/availability.mdx new file mode 100644 index 000000000..767038674 --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/availability.mdx @@ -0,0 +1,25 @@ +--- +title: 可用性 +sidebar-title: 可用性 +headline: 可用性 (OpenRPC) +description: 使用 `x-fern-availability` 扩展标记特定 SDK 版本中可用的功能 +--- + +在特定 SDK 版本中标记功能为可用: + +```yaml title="openrpc.yml" {4-6} +methods: + - name: beta.advancedSearch + summary: Advanced search functionality + x-fern-availability: + status: beta + message: "This feature is in beta and may change" + params: + - name: searchQuery + schema: + $ref: '#/components/schemas/AdvancedSearchQuery' + result: + name: searchResults + schema: + $ref: '#/components/schemas/SearchResults' +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/examples.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/examples.mdx new file mode 100644 index 000000000..01618218e --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/examples.mdx @@ -0,0 +1,53 @@ +--- +title: 请求 + 响应示例 +headline: 请求 + 响应示例 (OpenRPC) +description: 使用 x-fern-examples 为 OpenRPC 方法添加多个请求和响应示例。通过真实的用例改善您的 SDK 文档。 +og:title: OpenRPC 请求 + 响应示例 +sidebar-title: 请求 + 响应示例 +--- + + +为更好的 SDK 文档提供额外的示例: + +```yaml title="openrpc.yml" {8-25} +methods: + - name: user.create + summary: Create a new user + params: + - name: userData + schema: + $ref: '#/components/schemas/CreateUserRequest' + required: true + x-fern-examples: + - name: StandardUser + description: Create a regular user account + params: + userData: + email: "john@example.com" + name: "John Doe" + role: "user" + result: + id: "user_123" + email: "john@example.com" + name: "John Doe" + role: "user" + createdAt: "2024-01-15T10:30:00Z" + - name: AdminUser + description: Create an admin user account + params: + userData: + email: "admin@example.com" + name: "Admin User" + role: "admin" + permissions: ["read", "write", "delete"] + result: + id: "user_456" + email: "admin@example.com" + name: "Admin User" + role: "admin" + createdAt: "2024-01-15T10:30:00Z" + result: + name: user + schema: + $ref: '#/components/schemas/User' +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/ignore.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/ignore.mdx new file mode 100644 index 000000000..41b0d5193 --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/ignore.mdx @@ -0,0 +1,31 @@ +--- +title: 忽略方法或模式 +headline: 忽略方法或模式 (OpenRPC) +description: 使用 `x-fern-ignore` 扩展跳过读取特定 OpenRPC 元素 +sidebar-title: 忽略方法或模式 +--- + +使用 `x-fern-ignore` 从 SDK 生成中排除特定方法或模式: + +```yaml title="openrpc.yml" {4-5, 12-13} +methods: + - name: debug.internalMethod + summary: Internal debugging method + x-fern-ignore: true + params: + - name: debugData + schema: + type: object + result: + name: debugResult + schema: + type: object + - name: test.experimentalFeature + summary: Experimental feature (not ready for public use) + x-fern-ignore: true + params: [] + result: + name: result + schema: + type: string +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/method-names.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/method-names.mdx new file mode 100644 index 000000000..e4076294d --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/method-names.mdx @@ -0,0 +1,230 @@ +--- +title: SDK 方法名称 +headline: SDK 方法名称 (OpenRPC) +description: 在 OpenRPC 规范中控制 SDK 方法名称。使用 `x-fern-sdk-method-name` 为您的 JSON-RPC API 定义直观的、特定语言的方法名称。 +sidebar-title: SDK 方法名称 +--- + +默认情况下,Fern 基于您的 OpenRPC 方法名称生成 SDK 方法名称。您可以使用 `x-fern-sdk-method-name` 扩展来覆盖此行为。 + +## 自定义方法名称 + +使用 `x-fern-sdk-method-name` 为您的 JSON-RPC 方法指定自定义方法名称: + +```yaml title="openrpc.yml" {4-5, 12-13} +methods: + - name: user.getById + summary: Get user by ID + x-fern-sdk-method-name: getUser + params: + - name: id + schema: + type: string + required: true + result: + name: user + schema: + $ref: '#/components/schemas/User' + - name: order.createNew + summary: Create a new order + x-fern-sdk-method-name: create + params: + - name: orderData + schema: + $ref: '#/components/schemas/CreateOrderRequest' + required: true + result: + name: order + schema: + $ref: '#/components/schemas/Order' +``` + +这将生成如下的 SDK 方法: + +```typescript +// 代替 client.user.getById() +const user = await client.user.getUser({ id: "user_123" }); + +// 代替 client.order.createNew() +const order = await client.order.create({ orderData: {...} }); +``` + +## 方法命名约定 + +在命名 SDK 方法时遵循这些约定: + +### CRUD 操作 +使用标准的 CRUD 命名: + +```yaml title="openrpc.yml" {4-5} +methods: + - name: user.createUser + summary: Create a new user + x-fern-sdk-method-name: create + # 生成: client.user.create() + + - name: user.getUserById + summary: Get user by ID + x-fern-sdk-method-name: get + # 生成: client.user.get() + + - name: user.updateUser + summary: Update user information + x-fern-sdk-method-name: update + # 生成: client.user.update() + + - name: user.deleteUser + summary: Delete a user + x-fern-sdk-method-name: delete + # 生成: client.user.delete() +``` + +### 列表操作 +为列表操作使用描述性名称: + +```yaml title="openrpc.yml" {4-5} +methods: + - name: user.getAllUsers + summary: Get all users + x-fern-sdk-method-name: list + # 生成: client.user.list() + + - name: user.searchUsers + summary: Search for users + x-fern-sdk-method-name: search + # 生成: client.user.search() + + - name: order.getUserOrders + summary: Get orders for a user + x-fern-sdk-method-name: listByUser + # 生成: client.order.listByUser() +``` + +### 动作操作 +使用面向动作的名称: + +```yaml title="openrpc.yml" {4-5} +methods: + - name: email.sendNotification + summary: Send email notification + x-fern-sdk-method-name: send + # 生成: client.email.send() + + - name: payment.processPayment + summary: Process a payment + x-fern-sdk-method-name: process + # 生成: client.payment.process() + + - name: cache.invalidateCache + summary: Invalidate cache entries + x-fern-sdk-method-name: invalidate + # 生成: client.cache.invalidate() +``` + +## 特定语言的方法名称 + +您可以为不同的编程语言指定不同的方法名称: + +```yaml title="openrpc.yml" {4-9} +methods: + - name: user.getUserPreferences + summary: Get user preferences + x-fern-sdk-method-name: + python: get_preferences + typescript: getPreferences + go: GetPreferences + java: getPreferences + csharp: GetPreferences + params: + - name: userId + schema: + type: string + required: true + result: + name: preferences + schema: + $ref: '#/components/schemas/UserPreferences' +``` + +## 命名空间方法名称 + +对于带有命名空间前缀的方法,自定义最终的方法名称: + +```yaml title="openrpc.yml" {4-5, 12-13} +methods: + - name: analytics.track.pageView + summary: Track page view event + x-fern-sdk-method-name: trackPageView + params: + - name: eventData + schema: + $ref: '#/components/schemas/PageViewEvent' + required: true + - name: analytics.track.conversion + summary: Track conversion event + x-fern-sdk-method-name: trackConversion + params: + - name: eventData + schema: + $ref: '#/components/schemas/ConversionEvent' + required: true +``` + +生成: +```typescript +await client.analytics.trackPageView({ eventData: {...} }); +await client.analytics.trackConversion({ eventData: {...} }); +``` + +## 通知方法名称 + +对于通知方法(单向调用),使用适当的命名: + +```yaml title="openrpc.yml" {4-5, 12-13} +methods: + - name: log.recordError + summary: Record an error event + x-fern-sdk-method-name: logError + params: + - name: errorData + schema: + $ref: '#/components/schemas/ErrorData' + required: true + # 无结果 - 这是一个通知 + - name: metrics.incrementCounter + summary: Increment a metric counter + x-fern-sdk-method-name: increment + params: + - name: metric + schema: + type: string + required: true + - name: value + schema: + type: number + default: 1 + # 无结果 - 这是一个通知 +``` + +## 异步方法命名 + +对于返回 promise 或 future 的方法,考虑异步命名: + +```yaml title="openrpc.yml" {4-5} +methods: + - name: report.generateReport + summary: Generate a report (long-running) + x-fern-sdk-method-name: generateAsync + params: + - name: reportConfig + schema: + $ref: '#/components/schemas/ReportConfig' + required: true + result: + name: jobId + schema: + type: string + description: Job ID for tracking report generation +``` + +这确保方法名称遵循每个目标语言的约定,同时为开发者维护清晰直观的 API。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/sdk-group-name.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/sdk-group-name.mdx new file mode 100644 index 000000000..daed5de4a --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/sdk-group-name.mdx @@ -0,0 +1,37 @@ +--- +title: SDK 组名称 +sidebar-title: SDK 组名称 +headline: SDK 组名称 (OpenRPC) +description: 学习如何在 OpenRPC 中使用 x-fern-sdk-group-name 将相关方法组织成逻辑组,以获得更清洁、更直观的 SDK 代码。 +--- + +在 SDK 中将相关方法分组: + +```yaml title="openrpc.yml" {4-5, 12-13} +methods: + - name: user.create + summary: Create user + x-fern-sdk-group-name: users + params: [...] + result: {...} + + - name: user.get + summary: Get user + x-fern-sdk-group-name: users + params: [...] + result: {...} + + - name: order.create + summary: Create order + x-fern-sdk-group-name: orders + params: [...] + result: {...} +``` + +这会生成具有分组方法的 SDK: + +```typescript +client.users.create(...) +client.users.get(...) +client.orders.create(...) +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/extensions/server-name.mdx b/fern/translations/zh/products/api-def/openrpc/extensions/server-name.mdx new file mode 100644 index 000000000..5b0bd20df --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/extensions/server-name.mdx @@ -0,0 +1,20 @@ +--- +title: 服务器名称 +headline: 服务器名称 (OpenRPC) +description: 使用 `x-fern-server-name` 扩展为服务器指定自定义名称 +sidebar-title: 服务器名称 +--- + +为服务器指定自定义名称: + +```yaml title="openrpc.yml" {3-4} +servers: + - name: production + url: https://api.yourcompany.com/rpc + x-fern-server-name: Production + description: Production JSON-RPC server + - name: staging + url: https://staging-api.yourcompany.com/rpc + x-fern-server-name: Staging + description: Staging environment +``` \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/methods/rpc-methods.mdx b/fern/translations/zh/products/api-def/openrpc/methods/rpc-methods.mdx new file mode 100644 index 000000000..088490d5b --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/methods/rpc-methods.mdx @@ -0,0 +1,278 @@ +--- +title: JSON-RPC 方法 +subtitle: 使用参数、结果和错误处理来记录 JSON-RPC 方法 +sidebar-title: JSON-RPC 方法 +--- + +OpenRPC 中的方法是 JSON-RPC API 的核心构建块。每个方法都定义了过程名称、参数、返回值和潜在错误。 + +```yml title="openrpc.yml" {2-19} +methods: + - name: user.create + summary: Create a new user + description: Creates a new user account with the provided information + params: + - name: userData + schema: + $ref: '#/components/schemas/CreateUserRequest' + required: true + description: User data for account creation + result: + name: createdUser + schema: + $ref: '#/components/schemas/User' + description: The newly created user object + examples: + - name: CreateUserExample + description: Example of creating a user + params: + userData: + email: "john@example.com" + name: "John Doe" + age: 30 + result: + id: "user_123" + email: "john@example.com" + name: "John Doe" + age: 30 + createdAt: "2024-01-15T10:30:00Z" +``` + +## 方法参数 + +JSON-RPC 中的参数可以是位置参数(按位置)或命名参数(按名称): + +### 命名参数 +```yml title="openrpc.yml" {4-14} +methods: + - name: calculate.add + summary: Add two numbers + paramStructure: by-name + params: + - name: a + schema: + type: number + required: true + description: First number + - name: b + schema: + type: number + required: true + description: Second number + result: + name: sum + schema: + type: number +``` + +### 位置参数 +```yml title="openrpc.yml" {4-12} +methods: + - name: calculate.multiply + summary: Multiply two numbers + paramStructure: by-position + params: + - schema: + type: number + description: First number (multiplicand) + - schema: + type: number + description: Second number (multiplier) + result: + name: product + schema: + type: number +``` + +## 复杂参数类型 + +为方法参数使用复杂的模式: + +```yml title="openrpc.yml" {6-24} +methods: + - name: order.create + summary: Create a new order + description: Creates a new order with items and shipping information + params: + - name: orderData + schema: + type: object + properties: + items: + type: array + items: + $ref: '#/components/schemas/OrderItem' + minItems: 1 + shippingAddress: + $ref: '#/components/schemas/Address' + paymentMethod: + type: string + enum: [credit_card, paypal, bank_transfer] + notes: + type: string + maxLength: 500 + required: + - items + - shippingAddress + - paymentMethod + required: true + result: + name: order + schema: + $ref: '#/components/schemas/Order' +``` + +## 方法结果 + +定义成功方法响应的结构: + +```yml title="openrpc.yml" {8-20} +methods: + - name: search.products + summary: Search for products + params: + - name: query + schema: + type: string + required: true + result: + name: searchResults + schema: + type: object + properties: + products: + type: array + items: + $ref: '#/components/schemas/Product' + totalCount: + type: integer + hasMore: + type: boolean + nextCursor: + type: string + required: + - products + - totalCount + - hasMore +``` + +## 错误处理 + +为您的方法定义自定义错误: + +```yml title="openrpc.yml" {10-24} +methods: + - name: user.login + summary: Authenticate user + params: + - name: email + schema: + type: string + format: email + required: true + errors: + - code: -32001 + message: Invalid credentials + data: + type: object + properties: + error: + type: string + const: "Email or password is incorrect" + - code: -32002 + message: Account locked + data: + type: object + properties: + error: + type: string + const: "Account temporarily locked due to failed login attempts" + unlockTime: + type: string + format: date-time + result: + name: authResult + schema: + $ref: '#/components/schemas/AuthToken' +``` + +## 方法示例 + +提供全面的示例以获得更好的文档: + +```yml title="openrpc.yml" {12-35} +methods: + - name: file.upload + summary: Upload a file + params: + - name: fileData + schema: + type: object + properties: + filename: + type: string + content: + type: string + format: base64 + mimeType: + type: string + required: true + examples: + - name: ImageUpload + description: Upload a JPEG image + params: + fileData: + filename: "photo.jpg" + content: "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC/gA==" + mimeType: "image/jpeg" + result: + fileId: "file_abc123" + url: "https://cdn.example.com/files/file_abc123.jpg" + size: 1024000 + - name: DocumentUpload + description: Upload a PDF document + params: + fileData: + filename: "document.pdf" + content: "JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCgoyIDAgb2JqCjw8Ci9UeXBlIC9QYWdlcwo=" + mimeType: "application/pdf" + result: + fileId: "file_def456" + url: "https://cdn.example.com/files/file_def456.pdf" + size: 256000 +``` + +## 可选参数 + +定义带有默认值的可选参数: + +```yml title="openrpc.yml" {6-19} +methods: + - name: report.generate + summary: Generate a report + params: + - name: reportType + schema: + type: string + enum: [daily, weekly, monthly, yearly] + required: true + - name: format + schema: + type: string + enum: [pdf, csv, json] + default: pdf + required: false + description: Output format (defaults to PDF) + - name: includeCharts + schema: + type: boolean + default: true + required: false + description: Include charts in the report + result: + name: report + schema: + $ref: '#/components/schemas/Report' +``` + +方法是 JSON-RPC API 的基础,提供了一个清晰的契约来说明哪些操作是可用的、它们期望什么数据以及返回什么。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/overview.mdx b/fern/translations/zh/products/api-def/openrpc/overview.mdx new file mode 100644 index 000000000..cbfe5a3ba --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/overview.mdx @@ -0,0 +1,210 @@ +--- +title: 什么是 OpenRPC 规范? +description: 了解如何使用 OpenRPC 规范与 Fern 来文档化 JSON-RPC API。设置您的 fern 文件夹并配置 OpenRPC v1.3.2 或 v1.2.6。 +sidebar-title: 什么是 OpenRPC 规范? +--- + + + + +OpenRPC 规范是开发人员用来文档化 JSON-RPC API 的框架。该规范使用 JSON 或 YAML 编写,包含您的所有方法、参数、模式和服务器配置。Fern 兼容 OpenRPC 规范 [v1.3.2](https://spec.open-rpc.org/) 和 [v1.2.6](https://github.com/open-rpc/spec/releases/tag/1.2.6)。 + +以下是 OpenRPC 文件的示例: + +```yaml openrpc.yml +openrpc: 1.3.2 +info: + title: Calculator API + version: 1.0.0 + description: | + A simple calculator API that performs basic arithmetic operations + using JSON-RPC 2.0 protocol. +servers: + - name: production + url: https://api.calculator.com/rpc + description: Production JSON-RPC server + - name: development + url: http://localhost:8080/rpc + description: Development server +methods: + - name: add + summary: Add two numbers + description: Performs addition of two numeric values + params: + - name: a + schema: + type: number + required: true + description: First number to add + - name: b + schema: + type: number + required: true + description: Second number to add + result: + name: sum + schema: + type: number + description: The sum of the two numbers + examples: + - name: AddExample + description: Example of adding two numbers + params: + a: 5 + b: 3 + result: 8 + - name: divide + summary: Divide two numbers + description: Performs division of two numeric values + params: + - name: dividend + schema: + type: number + required: true + description: Number to be divided + - name: divisor + schema: + type: number + required: true + description: Number to divide by + result: + name: quotient + schema: + type: number + description: The result of the division + errors: + - code: -32602 + message: Division by zero + data: + type: object + properties: + error: + type: string + const: "Cannot divide by zero" + examples: + - name: DivideExample + description: Example of dividing two numbers + params: + dividend: 10 + divisor: 2 + result: 5 + - name: notify_calculation + summary: Notify about calculation + description: Send a notification about a completed calculation (no response expected) + params: + - name: operation + schema: + type: string + enum: [add, subtract, multiply, divide] + required: true + - name: result + schema: + type: number + required: true + - name: timestamp + schema: + type: string + format: date-time + required: true +components: + schemas: + CalculationRequest: + type: object + properties: + operation: + type: string + enum: [add, subtract, multiply, divide] + operands: + type: array + items: + type: number + minItems: 2 + maxItems: 2 + precision: + type: integer + minimum: 0 + maximum: 10 + default: 2 + required: + - operation + - operands + CalculationResult: + type: object + properties: + result: + type: number + operation: + type: string + timestamp: + type: string + format: date-time + required: + - result + - operation + - timestamp +``` + +## 设置您的 fern 文件夹 + + 正在考虑生成 OpenRPC 规范的选项?在[这里](https://fern-community.slack.com/join/shared_invite/zt-2dpftfmif-MuAegl8AfP_PK8s2tx350Q%EF%BB%BF#/shared-invite/email)获取实时支持 + + + +在项目根目录中创建一个 `fern/` 文件夹。 + +``` +fern/ +``` + + +将您的 OpenRPC 规范添加到 fern 目录中。您可以将其放在名为 `openrpc` 的子文件夹中,或直接放在 fern 目录中。 + +``` +fern/ + └─ openrpc/ + └─ openrpc.yml +``` + + +在您的 fern 目录中添加一个 `fern.config.json` 文件,列出您的组织和当前版本的 Fern CLI: + +```json title="fern.config.json" +{ + "organization": "your-organization", + "version": "" +} +``` + +``` +fern/ + ├─ fern.config.json + └─ openrpc/ + └─ openrpc.yml +``` + + +在您的 fern 目录中创建一个 `generators.yml` 文件,并添加对您的 OpenRPC 规范的引用: + +```yaml title="generators.yml" +# Your API definition +api: + specs: + - openrpc: ./openrpc/openrpc.yml + +groups: + external: + generators: + # Your generator configurations here +``` + +您的最终目录结构: + +``` +fern/ + ├─ fern.config.json + ├─ generators.yml + └─ openrpc/ + └─ openrpc.yml +``` + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/openrpc/servers.mdx b/fern/translations/zh/products/api-def/openrpc/servers.mdx new file mode 100644 index 000000000..b29d298d4 --- /dev/null +++ b/fern/translations/zh/products/api-def/openrpc/servers.mdx @@ -0,0 +1,196 @@ +--- +title: 服务器 +description: 为您的 JSON-RPC API 配置服务器 URL 和传输协议 +subtitle: 为您的 JSON-RPC API 定义服务器 URL 和传输机制 +sidebar-title: 服务器 +--- + + +OpenRPC 允许您指定一个或多个服务器配置,定义客户端如何连接到您的 JSON-RPC API。 + +```yml openrpc.yml +servers: + - name: production + url: https://api.yourcompany.com/rpc + description: Production HTTP JSON-RPC server + - name: websocket + url: wss://api.yourcompany.com/rpc + description: Production WebSocket JSON-RPC server +``` + +指定服务器对于 SDK 和文档都很有价值: +- 对于 SDK,您的用户无需在客户端实例化时手动指定服务器 URL +- 对于文档,您的 API playground 将自动连接到正确的服务器 + +## 传输协议 + +JSON-RPC 可以通过各种传输协议使用: + +### HTTP/HTTPS 传输 +```yml openrpc.yml +servers: + - name: http-production + url: https://api.yourcompany.com/rpc + description: HTTPS JSON-RPC endpoint + - name: http-staging + url: https://staging-api.yourcompany.com/rpc + description: Staging HTTPS endpoint +``` + +### WebSocket 传输 +```yml openrpc.yml +servers: + - name: websocket-production + url: wss://api.yourcompany.com/rpc + description: WebSocket JSON-RPC for real-time communication + - name: websocket-dev + url: ws://localhost:8080/rpc + description: Development WebSocket server +``` + +### TCP 传输 +```yml openrpc.yml +servers: + - name: tcp-production + url: tcp://api.yourcompany.com:9090 + description: TCP JSON-RPC server for high-performance applications +``` + +## 服务器变量 + +使用变量使您的服务器配置更加灵活: + +```yml openrpc.yml {3-12} +servers: + - name: production + url: https://{environment}.api.yourcompany.com/rpc + description: Production JSON-RPC server + variables: + environment: + default: prod + enum: + - prod + - staging + - dev + description: Environment name +``` + +## 多环境配置 + +配置不同的环境并提供适当的描述: + +```yml openrpc.yml +servers: + - name: production + url: https://api.yourcompany.com/rpc + description: Production environment - high availability, rate limited + - name: staging + url: https://staging-api.yourcompany.com/rpc + description: Staging environment - for testing new features + - name: development + url: http://localhost:8080/rpc + description: Local development server + - name: sandbox + url: https://sandbox-api.yourcompany.com/rpc + description: Sandbox environment - safe for testing +``` + +## 服务器特定配置 + +添加服务器特定的元数据和配置: + +```yml openrpc.yml {4-12} +servers: + - name: production + url: https://api.yourcompany.com/rpc + description: Production JSON-RPC server + variables: + version: + default: v1 + enum: [v1, v2] + description: API version + summary: High-performance production server + tags: + - name: production + description: Production environment +``` + +## 负载均衡和多个 URL + +为负载均衡配置多个服务器 URL: + +```yml openrpc.yml +servers: + - name: primary + url: https://api-primary.yourcompany.com/rpc + description: Primary production server + - name: secondary + url: https://api-secondary.yourcompany.com/rpc + description: Secondary production server (fallback) + - name: regional-us + url: https://us.api.yourcompany.com/rpc + description: US regional server + - name: regional-eu + url: https://eu.api.yourcompany.com/rpc + description: EU regional server +``` + +## WebSocket 特定配置 + +配置带有连接参数的 WebSocket 服务器: + +```yml openrpc.yml {4-10} +servers: + - name: websocket-main + url: wss://api.yourcompany.com/rpc + description: Main WebSocket JSON-RPC server + variables: + protocol: + default: "jsonrpc-2.0" + description: JSON-RPC protocol version + heartbeat: + default: "30" + description: Heartbeat interval in seconds +``` + +## 安全配置 + +在服务器级别配置身份验证和安全性: + +```yml openrpc.yml {4-6} +servers: + - name: secure-production + url: https://api.yourcompany.com/rpc + description: Secure production server with authentication + security: + - bearerAuth: [] + - apiKeyAuth: [] +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + apiKeyAuth: + type: apiKey + in: header + name: X-API-Key +``` + +## 传输特定优化 + +为特定用例配置服务器: + +```yml openrpc.yml +servers: + - name: bulk-operations + url: https://bulk.api.yourcompany.com/rpc + description: Optimized for batch requests and bulk operations + - name: real-time + url: wss://realtime.api.yourcompany.com/rpc + description: WebSocket server optimized for real-time notifications + - name: analytics + url: https://analytics.api.yourcompany.com/rpc + description: Dedicated server for analytics and reporting methods +``` + +服务器配置帮助客户端了解如何连接到您的 JSON-RPC API,以及哪种传输机制最适合他们的用例。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/asyncapi-empty.mdx b/fern/translations/zh/products/api-def/pages/asyncapi-empty.mdx new file mode 100644 index 000000000..4fd67a668 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/asyncapi-empty.mdx @@ -0,0 +1,8 @@ +--- +title: AsyncAPI 定义重定向页面 +description: 这是一个用于重定向到 AsyncAPI 定义页面的空页面。 +sidebar-title: AsyncAPI 定义重定向页面 +--- + + +此页面为空。请访问 [AsyncAPI 定义](/api-definitions/asyncapi/overview) 页面获取更多信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/ferndef-empty.mdx b/fern/translations/zh/products/api-def/pages/ferndef-empty.mdx new file mode 100644 index 000000000..1e4382560 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/ferndef-empty.mdx @@ -0,0 +1,8 @@ +--- +title: Fern 定义重定向页面 +description: 这是一个用于重定向到 Fern 定义页面的空白页面。 +sidebar-title: Fern 定义重定向页面 +--- + + +此页面为空。请访问 [Fern 定义](/api-definitions/ferndef/overview) 页面获取更多信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/grpc-empty.mdx b/fern/translations/zh/products/api-def/pages/grpc-empty.mdx new file mode 100644 index 000000000..0c8da60a7 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/grpc-empty.mdx @@ -0,0 +1,8 @@ +--- +title: gRPC 定义重定向页面 +description: 这是一个用于重定向到 gRPC 定义页面的空页面。 +sidebar-title: gRPC 定义重定向页面 +--- + + +此页面为空。请访问 [gRPC 定义](/api-definitions/grpc/overview) 页面获取更多信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/openapi-empty.mdx b/fern/translations/zh/products/api-def/pages/openapi-empty.mdx new file mode 100644 index 000000000..37aa06162 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/openapi-empty.mdx @@ -0,0 +1,8 @@ +--- +title: OpenAPI 定义重定向页面 +description: 这是一个用于重定向到 OpenAPI 定义页面的空白页面。 +sidebar-title: OpenAPI 定义重定向页面 +--- + + +此页面为空。请访问 [OpenAPI 定义](/api-definitions/openapi/overview) 页面获取更多信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/openrpc-empty.mdx b/fern/translations/zh/products/api-def/pages/openrpc-empty.mdx new file mode 100644 index 000000000..24938a12c --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/openrpc-empty.mdx @@ -0,0 +1,8 @@ +--- +title: OpenRPC 定义重定向页面 +description: 这是一个用于重定向到 OpenRPC 定义页面的空页面。 +sidebar-title: OpenRPC 定义重定向页面 +--- + + +此页面为空。请访问 [OpenRPC 定义](/api-definitions/openrpc/overview) 页面获取更多信息。 \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/overrides.mdx b/fern/translations/zh/products/api-def/pages/overrides.mdx new file mode 100644 index 000000000..be985c1b2 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/overrides.mdx @@ -0,0 +1,191 @@ +--- +title: 覆盖 +description: 使用单独的覆盖文件自定义您的 API 定义。 +sidebar-title: 覆盖 +--- + +使用覆盖文件可以在不修改原始规范的情况下自定义您的 OpenAPI、AsyncAPI 或 OpenRPC 定义。这在以下情况下很有用: + +* 您的 API 规范是从服务器代码自动生成的 +* 您需要为 SDK 和文档配置不同的设置 + +覆盖功能适用于 OpenAPI、AsyncAPI 和 OpenRPC 规范。对于 [Fern Definition](/api-definitions/ferndef/overview) 不需要覆盖,因为您可以直接编辑这些文件。 + + + 对于 OpenAPI 规范,Fern 建议使用 [叠加层](/learn/api-definitions/openapi/overlays) 而不是覆盖。叠加层遵循官方 [OpenAPI 叠加层规范](https://spec.openapis.org/overlay/v1.0.0.html),支持使用 JSONPath 通配符进行批量更改,并且在 OpenAPI 生态系统中具有可移植性。 + + 覆盖功能也完全受支持。如果覆盖对您的团队有效,则无需切换。您也可以同时使用两者(首先应用覆盖,然后应用叠加层)。 + + +## 实现覆盖 + + + + +在包含您的 API 定义的文件夹中[创建一个 `overrides.yml` 文件](/cli-api-reference/cli-reference/commands#fern-write-overrides): + +```bash {6} +fern/ + ├─ fern.config.json + ├─ generators.yml + └─ spec-folder/ + ├─ spec-file.yml # API 定义 + └─ overrides.yml +``` + + 覆盖文件的格式独立于规范。例如,即使您的 OpenAPI 规范是 JSON 格式,您也可以用 yaml 编写覆盖。 + + + + +对于 OpenAPI、AsyncAPI 和 OpenRPC,您可以使用 [Fern 的扩展](/learn/api-definitions/asyncapi/overrides#definition-specific-extensions) 来应用自定义。 + +```yml title="overrides.yml" {4-5} +paths: + /users: + post: + x-fern-sdk-group-name: users + x-fern-sdk-method-name: create +``` + + + + +```yaml title="generators.yml" +api: + specs: + - openapi: spec-file.yml + overrides: ./overrides.yml +``` + +您也可以指定[多个覆盖文件](#managing-overrides-across-apis)。 + + + +现在当您运行 `fern generate` 时,Fern 会将您的原始 API 规范与覆盖文件合并: + + +```yml openapi.yml +paths: + /users: + post: + description: Create a User + operationId: users_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' +``` +```yml title="overrides.yml" {4-5} +paths: + /users: + post: + x-fern-sdk-group-name: users + x-fern-sdk-method-name: create +``` +```yml title="合并后" {4-5} +paths: + /users/post: + post: + x-fern-sdk-group-name: users + x-fern-sdk-method-name: create + description: Create a User + operationId: users_post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' +``` + + + + +## 跨 API 管理覆盖 + +`overrides` 字段接受单个路径或路径列表。当提供多个路径时,覆盖会按顺序依次应用,后面的文件对于冲突键会优先于前面的文件。 + + + + +当您的项目有多个需要相同基础覆盖的规范时,使用共享基础文件并在其上分层应用特定规范的覆盖。这避免了在每个规范中重复常见的覆盖(例如,SDK 方法名称或身份验证扩展): + +```yaml title="generators.yml" +api: + specs: + - openapi: ./payments-api.yml + overrides: + - ./shared-overrides.yml # 所有规范的通用覆盖 + - ./payments-overrides.yml # 支付特定的覆盖 + - openapi: ./users-api.yml + overrides: + - ./shared-overrides.yml # 相同的通用覆盖 + - ./users-overrides.yml # 用户特定的覆盖 +``` + + + + +当您需要为 SDK 生成和文档使用完全不同的覆盖时,使用带有各自 `generators.yml` 的单独文件夹。每个文件夹只引用与其关注点相关的覆盖: + + + + + + + + + + + + + + + + +```yaml title="sdks/generators.yml" +api: + specs: + - openapi: ../openapi.yml + overrides: sdk-overrides.yml +``` + +如果 SDK 和文档覆盖共享一个通用基础,您可以在每个文件夹内使用多个覆盖文件来避免重复: + +```yaml title="sdks/generators.yml" +api: + specs: + - openapi: ../openapi.yml + overrides: + - ../shared-overrides.yml # SDK 和文档的通用覆盖 + - sdk-overrides.yml # SDK 特定的覆盖 +``` + + + + +当您的公共和内部 API 共享相同的规范但需要不同的覆盖时,使用多个覆盖文件在公共基础上分层添加内部特定的内容。这在内部 API 是公共 API 超集时特别有用: + +```yaml title="generators.yml" {5, 12-13} +groups: + public: + specs: + - openapi: openapi.yml + overrides: public-overrides.yml + generators: + ... + internal: + specs: + - openapi: openapi.yml + overrides: + - public-overrides.yml # 基础:所有公共覆盖 + - internal-overrides.yml # 附加:仅内部覆盖 + generators: + ... +``` + +这避免了在内部配置中重复公共覆盖。`internal-overrides.yml` 文件只需包含内部 API 特定的添加内容。 + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/project-structure.mdx b/fern/translations/zh/products/api-def/pages/project-structure.mdx new file mode 100644 index 000000000..8b9908bd9 --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/project-structure.mdx @@ -0,0 +1,233 @@ +--- +title: 项目结构 +description: 描述 Fern 文件夹结构 +sidebar-title: 项目结构 +--- + +配置 fern 从 `fern` 文件夹开始,该文件夹包含你的 API 定义、SDK 生成器和你的 CLI 版本。 + +Fern 推荐使用多仓库结构,将你的 fern 文件夹放在源代码仓库中(包含你的 API 定义和生成配置),并[将每个生成的 SDK 放在其自己单独的仓库中](/sdks/overview/project-structure)。 + +## 目录结构 + +当你运行 `fern init`(对于 Fern Definition)或 `fern init --spec-type path/to/spec`(对于其他规范)时,你的 fern 文件夹将使用以下文件进行初始化: + + + + + + + + + + + +## 核心配置文件 + + + 除了核心文件之外,你可以选择使用 [overlays (OpenAPI)](/learn/api-definitions/openapi/overlays) 或 [overrides](/learn/api-definitions/asyncapi/overrides) 文件来自定义你的 API 定义,而无需修改原始规范。 + + +### `fern.config.json` + +`fern.config.json` 文件存储你的组织名称和 Fern CLI 版本。固定版本可以提供确定性构建。 + +```json title="fern.config.json" +{ + "organization": "plant-catalog", + "version": "" +} +``` + + + 当使用本地安装的 CLI 时,将 `version` 设置为 `"*"`。详细信息请参见[本地安装 Fern CLI](/cli-api-reference/cli-reference/overview#install-fern-cli-locally)。 + + +### `generators.yml` + +对于 OpenAPI/AsyncAPI,`generators.yml` 文件是必需的,用于声明你的 API 规范位置。这还启用了 [API 参考文档](/learn/docs/api-references/overview)。 + +```yaml title="generators.yml" +api: + specs: + - openapi: ./openapi/openapi.yml +``` + +**对于 Fern Definition**,不需要 `generators.yml` 来[生成 API 参考文档](/learn/docs/api-references/overview)。Fern 通过检查 `definition/` 目录自动检测你的 API。 + +对于 SDK 生成,所有规范格式都需要 `generators.yml`。添加 `groups` 部分来配置要生成哪些 SDK。详细信息请参见 [SDKs 项目结构](/sdks/overview/project-structure#generatorsyml)。 + +### API 定义文件 + +对于 Fern Definition,你的 API 配置分为两个文件:`api.yml` 用于 API 范围的配置,单独的 `.yml` 文件用于你的实际端点和类型定义。有关更多信息,请参见[什么是 Fern Definition?](/api-definitions/ferndef/overview)。 + +对于其他规范格式([OpenAPI](/api-definitions/openapi/overview)、[AsyncAPI](/api-definitions/asyncapi/overview)、[OpenRPC](/api-definitions/openrpc/overview) 和 [gRPC](/api-definitions/grpc/overview)),你将拥有一个独立的规范文件。 + +## 存储 API 定义的位置 + +有三种常见的管理 API 定义的方式: + +- **直接提交到你的 Fern 仓库(推荐)。** 将你的 API 定义文件检入包含你的 Fern 配置的同一个仓库中。如果你不在其他地方维护定义,这是最简单的方法。 +- **从源代码仓库同步。** 将你的 API 定义存储在与你的 API 源代码相同的仓库中,并将更新同步到你的 Fern 仓库。你可以使用 [`fern api update`](/cli-api-reference/cli-reference/commands#fern-api-update) CLI 命令或 [sync-openapi GitHub Action](/learn/api-definitions/openapi/sync-your-open-api-specification) 来自动化这个过程。 +- **在公共 URL 托管。** 从可公开访问的端点提供定义,并在 `generators.yml` 中配置 [`origin`](/learn/sdks/reference/generators-yml#openapi) 字段,以便 Fern 可以获取它。当你希望有一个多个消费者可以引用的单一规范定义时,这很有用。 + +## 多个 API + +Fern 支持两种处理多个 API 定义的方法。两种方法都需要一个 `apis` 文件夹 — 这个文件夹必须使用这个确切的名称。 + +1. **为每个 API 生成单独的 SDK** - 每个 API 生成自己独立的 SDK 包集合(例如,`@company/user-api`、`@company/payments-api` 或版本化的如 `@company/sdk-v1`、`@company/sdk-v2`) +2. **从多个 API 合并 SDK** - 多个 API 合并到一个 SDK 包中,并可选择使用命名空间(例如,`client.users`、`client.payments` 或版本化的如 `client.v1`、`client.v2`) + +### 为每个 API 生成单独的 SDK + +当每个 API 应该生成自己独立的 SDK 集合时使用这种方法。这适用于不同的 API(例如,`user-api` 和 `payments-api`)和版本化 API,你希望每个版本都可以独立安装(例如,`@company/sdk-v1`、`@company/sdk-v2`)。 + + + +将每个 API 或 API 版本放入 `fern/apis/` 的单独子文件夹中: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +每个 API 目录包含自己的 `generators.yml` 文件,引用同一文件夹中的规范: + +```yaml title="generators.yml" +api: + specs: + - openapi: openapi.yml # 同一文件夹中的规范文件 +groups: + # SDK 生成器配置 +``` + + + +### 从多个 API 合并 SDK + + + 此功能仅适用于 [Pro 和企业版计划](https://buildwithfern.com/pricing)。在 Pro 计划中,你可以将最多五个 API 合并到单个 SDK 中,在企业版计划中可以合并无限个 API。要开始使用,请联系 support@buildwithfern.com。 + + +当你想要将多个 API 合并到单个 SDK 包中,并可选择使用命名空间来组织它们时,使用这种方法。这适用于不同的 API 和版本化 API,但会增加包大小,因为所有 API 都捆绑在一起。 + +对于版本化 API,命名空间让你可以在同一个包中访问不同版本,如 `client.v1` 和 `client.v2`。 + + + +将每个 API 放入 `fern/apis/` 的单独子文件夹中: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +在单个 `generators.yml` 中列出所有 API: + +```yaml title="generators.yml" +api: + specs: + - openapi: user-api/openapi.yml + - openapi: payments-api/openapi.yml +groups: + # SDK 生成器配置 +``` + + + +添加 [`namespace`](/learn/sdks/reference/generators-yml#namespace) 来处理 API 之间重叠的模式名称或组织不同的 API 版本: + + + +```yaml title="generators.yml" {4} +api: + specs: + - openapi: apis/user-api/openapi.yml + - namespace: payments + openapi: apis/payments-api/openapi.yml +groups: + # SDK 生成器配置 +``` + + +在单个 `generators.yml` 中列出所有 API 版本并使用命名空间: + +```yaml title="generators.yml" {3, 5} +api: + specs: + - namespace: v1 + openapi: apis/v1/openapi.yml + - namespace: v2 + openapi: apis/v2/openapi.yml +groups: + # SDK 生成器配置 +``` + + + + \ No newline at end of file diff --git a/fern/translations/zh/products/api-def/pages/what-is-an-api-definition.mdx b/fern/translations/zh/products/api-def/pages/what-is-an-api-definition.mdx new file mode 100644 index 000000000..64c8066fe --- /dev/null +++ b/fern/translations/zh/products/api-def/pages/what-is-an-api-definition.mdx @@ -0,0 +1,353 @@ +--- +title: 什么是 API 定义? +description: 描述 API 提供方和 API 消费者之间的契约 +sidebar-title: 什么是 API 定义? +--- + + +API 定义是对您的 API 结构的机器可读规范,包括**端点**、**请求和响应模式**,以及**身份验证**要求。API 定义能够自动生成这些工件,而不需要手动保持 SDK 和文档与 API 变更的同步。 + +Fern 集成了多种 API 定义格式: + + + + OpenAPI 原称 Swagger,是最受欢迎的 API 定义格式。[OpenAPI](https://swagger.io/specification/) 可用于记录 RESTful API,并在 YAML 或 JSON 文件中定义。 + + 在[此处](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)查看 Petstore API 的 OpenAPI 规范示例 + + ```yaml maxLines={0} + openapi: 3.0.2 + tags: + - name: pet + description: Everything about your Pets + paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: Add a new pet to the store + operationId: addPet + requestBody: + description: Create a new pet in the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Pet' + responses: + '200': + description: Successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '405': + description: Invalid input + components: + schemas: + Pet: + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + example: 10 + name: + type: string + example: doggie + category: + $ref: '#/components/schemas/Category' + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/Tag' + xml: + name: tag + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: pet + type: object + ``` + + + [AsyncAPI](https://v2.asyncapi.com/docs) 是用于定义事件驱动 API 的规范。它用于记录使用 WebSocket、MQTT 和其他消息传递协议的 API。 + + 查看下面聊天应用程序的 AsyncAPI 规范示例: + + ```yaml maxLines={0} + asyncapi: 2.0.0 + info: + title: Chat server + version: 1.0.0 + + servers: + Production: + url: chat.com + protocol: ws + + channels: + "/application": + bindings: + ws: + query: + type: object + properties: + apiKey: + type: string + description: The API key for the client + minimum: 1 + bindingVersion: 0.1.0 + subscribe: + operationId: sendMessage + message: + $ref: '#/components/messages/SendMessage' + publish: + operationId: receiveMessage + message: + $ref: '#/components/messages/ReceiveMessage' + + components: + messages: + SendMessage: + payload: + message: string + ReceiveMessage: + payload: + message: string + from: + type: string + description: The userId for the sender of the message + ``` + + + + [OpenRPC](https://open-rpc.org/) 是用于描述 JSON-RPC 2.0 API 的规范。它为 JSON-RPC 生态系统提供了交互式文档和代码生成工具。 + + 查看下面加密钱包服务的 OpenRPC 规范示例: + + ```yaml maxLines=0 + # yaml-language-server: $schema=https://meta.open-rpc.org/ + $schema: https://meta.open-rpc.org/ + openrpc: 1.2.4 + info: + title: Basic Wallet API JSON-RPC Specification + description: A simple JSON-RPC API for querying wallet balances. + version: 0.0.1 + servers: + - url: https://wallet.example.com/json-rpc + name: Mainnet + methods: + - name: getBalance + description: Get the balance of a wallet address. + params: + - name: address + required: true + description: The wallet address to query. + schema: + type: string + result: + name: balance + description: The balance of the wallet address. + schema: + type: number + examples: + - name: getBalance example + params: + - name: address + value: "0x1234567890abcdef1234567890abcdef12345678" + result: + name: balance + value: 42.5 + ``` + + + [gRPC](https://grpc.io/) 是由 Google 开发的现代开源 RPC 框架。它使用 Protocol Buffers 作为接口定义语言,支持多种编程语言,并具有高效的二进制序列化功能。 + + gRPC API 使用 Protocol Buffer(.proto)文件定义,这些文件指定服务和消息类型。查看下面的 gRPC 服务定义示例: + + ```proto maxLines={0} + syntax = "proto3"; + + package petstore; + + // The pet store service definition + service PetStoreService { + // Get a pet by ID + rpc GetPet(GetPetRequest) returns (Pet); + + // Add a new pet to the store + rpc AddPet(AddPetRequest) returns (Pet); + + // List all pets with optional filtering + rpc ListPets(ListPetsRequest) returns (ListPetsResponse); + } + + // Request message for getting a pet + message GetPetRequest { + int64 pet_id = 1; + } + + // Request message for adding a pet + message AddPetRequest { + string name = 1; + string category = 2; + repeated string photo_urls = 3; + PetStatus status = 4; + } + + // Request message for listing pets + message ListPetsRequest { + int32 page_size = 1; + string page_token = 2; + PetStatus status = 3; + } + + // Response message for listing pets + message ListPetsResponse { + repeated Pet pets = 1; + string next_page_token = 2; + } + + // Pet message definition + message Pet { + int64 id = 1; + string name = 2; + string category = 3; + repeated string photo_urls = 4; + PetStatus status = 5; + } + + // Pet status enumeration + enum PetStatus { + PET_STATUS_UNSPECIFIED = 0; + PET_STATUS_AVAILABLE = 1; + PET_STATUS_PENDING = 2; + PET_STATUS_SOLD = 3; + } + ``` + + + Fern Definition 是我们对更简单 API 定义格式的理解。它采用**最佳实践**设计,同时支持 **RESTful 和事件驱动 API**,并针对 **SDK 生成**进行了优化。 + + + Fern Definition 受到了 [Amazon](https://smithy.io/2.0/index.html)、[Google](https://grpc.io/)、[Palantir](https://blog.palantir.com/introducing-conjure-palantirs-toolchain-for-http-json-apis-2175ec172d32)、Twilio 和 Stripe 等公司内部 API Definition 格式的启发。这些公司**拒绝了** OpenAPI 并构建了自己的版本。 + + + 查看下面的 Fern Definition 示例: + + ```yaml maxLines={0} + types: + MovieId: string + + Movie: + properties: + id: MovieId + title: string + rating: + type: double + docs: The rating scale is one to five stars + + CreateMovieRequest: + properties: + title: string + rating: double + + service: + auth: false + base-path: /movies + endpoints: + createMovie: + docs: Add a movie to the database + method: POST + path: /create-movie + request: CreateMovieRequest + response: MovieId + + getMovie: + method: GET + path: /{movieId} + path-parameters: + movieId: MovieId + response: Movie + errors: + - MovieDoesNotExistError + + errors: + MovieDoesNotExistError: + status-code: 404 + type: MovieId + ``` + + + + +## 为什么要创建 API 定义? + +一旦您拥有 API 定义,Fern 就会将其用作输入来生成诸如 SDK 和 API 参考文档等工件。每当您更新 API 定义时,都可以重新生成这些工件以确保它们始终保持最新状态。 + + + + 多语言客户端库,自动与您的 API 保持同步。 + + + 具有代码示例和实时测试功能的交互式 API 文档。 + + } + href="/docs/integrations/postman" + > + 预填充示例请求和响应的即用型集合。 + + } + href="/api-definitions/openapi/frameworks/fastapi" + > + 用于 FastAPI 的 Pydantic 模型或用于 Spring Boot 应用程序的控制器。 + + \ No newline at end of file diff --git a/fern/translations/zh/products/cli-api-reference/pages/cli-get-started.mdx b/fern/translations/zh/products/cli-api-reference/pages/cli-get-started.mdx new file mode 100644 index 000000000..374b530c8 --- /dev/null +++ b/fern/translations/zh/products/cli-api-reference/pages/cli-get-started.mdx @@ -0,0 +1,106 @@ +--- +title: 概述 +subtitle: 通过命令行管理和配置您的 Fern 项目。 +description: 了解如何使用 Fern CLI 管理 API 项目、验证定义、预览更改以及发布文档和 SDK。 +sidebar-title: 概述 +--- + +Fern CLI 让您能够初始化项目、验证 API 定义、在本地预览更改,以及发布文档和 SDK。本页介绍安装和常用命令,帮助您快速入门。 + +## 安装 + + + + +Fern CLI 需要 [Node.js](https://nodejs.org/) 版本 22 或更高版本,以及 npm 版本 10.0.0 或更高版本(随 Node.js 22 一起发布)。 + +```bash +node -v # 必须是 v22.0.0 或更高版本 +``` + +如果您的版本低于最低要求,请[下载最新的 Node.js LTS](https://nodejs.org/),它包含兼容的 npm 版本。 + + + + + +运行以下命令从 [npm](https://www.npmjs.com/) 全局安装 Fern CLI: + +```bash +npm install -g fern-api +fern -v # 验证安装 +``` + + + + + + +如果您希望将 Fern 作为项目依赖项管理,或者在网络连接有限的环境中工作,可以本地安装 CLI: + + + + +```bash +npm install fern-api +``` + + + + + +在您的 `fern.config.json` 中,将版本设置为 `*` 以使用本地安装的版本: + +```json fern.config.json {3} +{ + "organization": "your-org", + "version": "*" +} +``` + +有关更多详细信息,请参阅 [fern.config.json 参考](/learn/sdks/overview/project-structure#fernconfigjson)。 + + + + + +```bash +npm fern check +npm fern generate +``` + + + + + + +## 按产品分类的 CLI 工作流程 + +Fern CLI 管理文档和 SDK 工作流程。以下是每个产品常用命令的简要概述。所有命令都支持[全局选项](/learn/cli-api-reference/cli-reference/options),如用于调试的 `--log-level`。 + +有关完整的 CLI 文档,请参阅[命令参考](/learn/cli-api-reference/cli-reference/commands)。 + + + + 在使用 Fern Docs 时,您将使用这些 CLI 命令。有关完整说明,请参阅[文档快速入门](/learn/docs/getting-started/quickstart)。 + + | 命令 | 描述 | + |---------|-------------| + | `fern init --docs` | 创建新的文档项目 | + | `fern docs dev` | 在 `localhost:3000` 本地预览文档 | + | `fern generate --docs` | 将文档发布到生产环境 | + + + + 在使用 Fern SDK 时,您将使用这些 CLI 命令。有关完整说明,请参阅[SDK 快速入门](/learn/sdks/overview/quickstart)。 + + | 命令 | 描述 | + |---------|-------------| + | `fern init` | 创建新的 SDK 项目 | + | `fern check` | 验证您的 API 定义 | + | `fern generate --preview` | 在 `.preview/` 中本地预览 SDK | + | `fern generate` | 将 SDK 发布到生产环境 | + | `fern generate --group ` | 生成特定的 SDK 组 | + + + \ No newline at end of file diff --git a/fern/translations/zh/products/cli-api-reference/pages/commands.mdx b/fern/translations/zh/products/cli-api-reference/pages/commands.mdx new file mode 100644 index 000000000..1d2914ff8 --- /dev/null +++ b/fern/translations/zh/products/cli-api-reference/pages/commands.mdx @@ -0,0 +1,899 @@ +--- +title: '命令' +description: 'Fern CLI 所有命令的完整参考,用于生成 SDK 和开发者文档。' +subtitle: '了解 Fern CLI 命令。' +sidebar-title: '命令' +hideOnThisPage: true +--- + + +| 命令 | 描述 | +|---------|-------------| +| [`fern init`](#fern-init) | 从 OpenAPI 规范或从头开始创建新的 Fern 项目 | +| [`fern check`](#fern-check) | 验证 API 定义和配置 | +| [`fern upgrade`](#fern-upgrade) | 将 Fern CLI 和生成器更新到最新版本 | +| [`fern login`](#fern-login) | 通过 GitHub、Google、Postman 或企业 SSO 登录 Fern CLI | +| [`fern logout`](#fern-logout) | 从 Fern CLI 注销 | +| [`fern export`](#fern-export) | 导出 API 的 OpenAPI 规范 | +| [`fern api update`](#fern-api-update) | 手动更新 OpenAPI 规范 | +| [`fern api enrich`](#fern-api-enrich) | 将覆盖文件中的 `x-fern-examples` 合并到原生 OpenAPI 示例中 | + +## 文档命令 + +| 命令 | 描述 | +|---------|-------------| +| [`fern docs dev`](#fern-docs-dev) | 运行本地文档预览服务器 | +| [`fern docs diff`](#fern-docs-diff) Beta | 生成预览和生产文档之间的视觉差异 | +| [`fern generate --docs`](#fern-generate---docs) | 构建并发布文档更新 | +| [`fern docs preview list`](#fern-docs-preview-list) | 列出所有预览部署 | +| [`fern docs preview delete`](#fern-docs-preview-delete) | 删除预览部署 | +| [`fern docs md check`](#fern-docs-md-check) | 验证文档文件中的 MDX 语法 | +| [`fern docs theme export`](#fern-docs-theme-export) | 从 `docs.yml` 导出主题兼容字段到独立目录 | +| [`fern docs theme upload`](#fern-docs-theme-upload) | 上传主题到 Fern 注册表 | +| [`fern docs theme list`](#fern-docs-theme-list) | 列出组织的所有主题 | + +## SDK 生成命令 + +| 命令 | 描述 | +|---------|-------------| +| [`fern generate`](#fern-generate) | 构建并发布 SDK 更新 | +| [`fern write-definition`](#fern-write-definition) | 将 OpenAPI 规范转换为 [Fern Definition](/learn/api-definitions/ferndef/overview) | +| [`fern write-overrides`](#fern-write-overrides) | 创建 OpenAPI 自定义 | +| [`fern generator upgrade`](#fern-generator-upgrade) | 将 SDK 生成器更新到最新版本 | + +## 详细命令文档 + + + + + 使用 `fern init` 在当前文件夹中初始化新的 Fern 工作空间。默认情况下,您将看到 IMDb API 示例。 + + + ```bash + fern init [--docs] [--openapi ] + ``` + + + 使用 OpenAPI 初始化时,您的项目结构将如下所示: + + + + + + + + + + + + 对于 Fern Definition 初始化(不使用 OpenAPI),您将看到以下结构: + + + + + + + + + + + + + ### openapi + + 使用 `--openapi` 从 OpenAPI 规范初始化项目: + + ```bash + # 从本地文件初始化 + fern init --openapi ./path/to/openapi.yml + + # 从 URL 初始化 + fern init --openapi https://link.buildwithfern.com/petstore-openapi + ``` + + ### docs + + 通过添加 `--docs`,您还将获得一个包含 API Reference 部分的示例文档网站。 + + ```bash + fern init --docs + ``` + + 添加的文件将包含: + + ```yaml docs.yaml + instances: + - url: https://your-organization.docs.buildwithfern.com + title: Your Organization | Documentation + navigation: + - api: API Reference + colors: + accent-primary: '#ffffff' + background: '#000000' + ``` + + 要发布 API 文档,请运行 [`fern generate --docs`](/learn/cli-api/cli-reference/commands#fern-generate---docs)。 + + ### mintlify + + 通过添加 `--mintlify PATH_TO_MINT_CONFIG`,CLI 将根据 `mint.json` 文件自动将您的 Mintlify 文档文件夹转换为 Fern 文档网站。 + + ```bash + fern init --mintlify PATH_TO_MINT_CONFIG + ``` + + CLI 将创建一个具有以下结构的 `fern/` 文件夹: + + + + + + + + + + ### readme + + `fern init` 命令支持导入 Readme 生成的文档网站。这需要安装本地 chromium 浏览器实例。 + 您可以通过从源代码安装 `fern` cli 来确保已安装,请按照[这里](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md)的说明进行操作。 + + 通过添加 `--readme URL_TO_README_DOCS_SITE`,CLI 将自动将 Readme 生成的文档网站转换为 Fern 文档网站。 + + ```bash + fern init --readme URL_TO_README_DOCS_SITE + ``` + + CLI 将创建一个具有以下结构的 `fern/` 文件夹: + + + + + + + + + + + 有关入门的更多信息,请查看我们的[快速入门指南](/learn/docs/getting-started/quickstart) + + + + + + 使用 `fern export` 为您的 API 生成 OpenAPI 规范。 + + 当您以 OpenAPI 以外的格式(例如 [Fern Definition](/api-definitions/ferndef/overview))定义了 API 并需要将其导出为 OpenAPI 规范以与其他工具或服务集成时,此命令非常有用。 + + + + ```bash + fern export [--api ] path/to/openapi.yml + fern export [--api ] path/to/openapi.json + ``` + + + ### api + + 当您在 `fern/apis/` 文件夹中定义了多个 API 时,使用 `--api` 指定要导出的 API。 + + + ```bash + fern export --api public-api path/to/openapi.yml + fern export --api public-api path/to/openapi.json + ``` + + + + + + 使用 `fern generate` 运行 Fern 编译器并为您的 API 创建 SDK。 + + + ```bash + fern generate [--group ] [--api ] [--version ] [--preview] [--fernignore ] [--local] [--force] + ``` + + + ### group + + 使用 `--group ` 指定要运行的生成器组。您可以使用组名或别名。别名在您的 [`generators.yml`](/learn/sdks/reference/generators-yml#aliases) 中定义并映射到多个组,允许您通过单个命令并行运行多个组。 + + ```bash + # 运行特定组 + fern generate --group python-sdk + + # 运行在"all"别名中定义的所有组 + fern generate --group all + + # 运行在"frontend"别名中定义的所有组 + fern generate --group frontend + + # 本地运行特定组(自托管) + fern generate --group python-sdk --local + ``` + + 您还可以在 `generators.yml` 中将别名设置为 `default-group`,这样运行不带任何参数的 `fern generate` 将运行该别名中的所有组。 + + `--group` 标志可以与其他标志组合使用,如用于[自托管 SDK 生成](/learn/sdks/deep-dives/self-hosted)的 `--local`、用于本地测试的 `--preview` 或用于指定 SDK 版本的 `--version`。 + + ### preview + + 使用 `--preview` 在发布前本地测试 SDK 更改。这在开发期间特别有用: + - 将 SDK 生成到本地 `.preview/` 文件夹 + - 允许在 Fern 定义上快速迭代 + - 不会将更改发布到包管理器或 GitHub + + ```bash + # 预览所有 SDK + fern generate --preview + + # 预览特定 SDK 组 + fern generate --group python-sdk --preview + ``` + + ### api + + 使用 `--api ` 为 SDK 生成指定 API。当项目包含多个 API 定义时这很有用。API 名称应与 `fern/apis/` 文件夹中的目录名称匹配。 + + ```bash + fern generate --api public-api + ``` + + ### version + + 使用 `--version` 指定 SDK 版本号,通常遵循语义版本控制(semver)格式(`MAJOR.MINOR.PATCH`)。这在发布 SDK 版本的 CI/CD 流水线中特别有用。 + + ```bash + # 使用版本 2.11.0 生成所有 SDK + fern generate --version 2.11.0 + + # 为支付 API 的 Python SDK 生成版本 1.2.3 + fern generate --api payments-api --group python-sdk --version 1.2.3 + ``` + + ### fernignore + + 使用 `--fernignore` 为 SDK 生成指定自定义 `.fernignore` 文件路径。这允许您临时测试不同的忽略配置,而无需修改存储库中已提交的 `.fernignore`。 + + Fern 将使用指定的文件而不是主分支上的文件,并将新的 `.fernignore` 作为生成过程的一部分提交到您的存储库。 + + ```bash + fern generate --fernignore ./custom-fernignore + ``` + + ### local + + 使用 `--local` 在您自己的机器上运行 SDK 生成,而不是使用 Fern 的云基础设施。这对于有严格安全或合规要求的组织很有用。请参阅[自托管 SDK](/learn/sdks/deep-dives/self-hosted) 获取设置说明。 + + ```bash + # 本地生成所有 SDK + fern generate --local + + # 本地生成特定 SDK 组 + fern generate --group python-sdk --local + ``` + + + 您的机器上必须运行 Docker 守护进程,因为 SDK 生成在 Docker 容器内运行。 + + + 默认情况下,CLI 从 Docker Hub 拉取生成器镜像。要从自定义容器注册表拉取,请在您的 `generators.yml` 中使用 [`image` 字段](/learn/sdks/reference/generators-yml#image) 而不是 `name`。请参阅[自定义容器注册表](/learn/sdks/deep-dives/self-hosted#optional-configure-a-custom-container-registry)了解详细信息。 + + ### force + + 使用 `--force` 跳过生成过程中的确认提示。这在会阻塞流水线的交互式提示的 CI/CD 环境中很有用。 + + ```bash + # 不带确认提示生成 + fern generate --group python-sdk --force + ``` + + + + + + + `--broken-links` 和 `--strict-broken-links` 标志已弃用。请在 `docs.yml` 中使用 [`broken-links` 验证规则](/learn/docs/configuration/site-level-settings#check-configuration)。 + + 您还可以使用 [Fern Dashboard](https://dashboard.buildwithfern.com/) 验证实时发布网站上的内部和外部链接。 + + + 使用 `fern check` 验证您的 API 定义和 Fern 配置,包括 [`fern.config.json`](/learn/sdks/overview/project-structure#fernconfigjson)、`generators.yml` 和 `docs.yml`。它检查损坏的链接、无效的 API 示例、配置错误等。当所有检查通过时,该命令不产生输出。 + + + ```bash + fern check [--api ] [--warnings] + ``` + + + 您可以在 `docs.yml` 文件中[使用 `check.rules` 属性](/learn/docs/configuration/site-level-settings#check-configuration)配置 `fern check` 运行的验证规则的严重性。 + + ```yaml docs.yml + check: + rules: + broken-links: error + example-validation: warn + missing-redirects: error + ``` + + ### api + + 使用 `--api ` 指定要检查的 API。 + + ```bash + fern check --api public-api + ``` + + ### warnings + + 使用 `--warnings` 除了记录错误外还记录警告。 + + ```bash + fern check --warnings + ``` + + ## 在 GitHub Action 中使用 + + + ```yml maxLines=14 + name: Fern Validation Check + + on: + pull_request: + push: + branches: + - main + + jobs: + validate-fern-api: + name: Validate using Fern's linter + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Fern CLI + run: npm install -g fern-api + + - name: Validate API with Fern + run: fern check + + ``` + + + + + + + 使用 `fern generate --docs` 为您的 API 创建文档网站。 + + + ```bash + fern generate --docs [instance ] [--preview] [--id ] [--force] + ``` + + + ### instance + + 使用 `--instance` 指定要为 `docs.yml` 中的哪个实例 URL 生成文档。 + + ```bash + fern generate --docs --instance your-organization.docs.buildwithfern.com + ``` + + ### preview + + 使用 `--preview` 在将更改发布到生产网站之前预览文档更新。 + + ```bash + fern generate --docs --preview + ``` + + ### id + + 使用 `--id` 与 `--preview` 一起创建稳定的命名预览链接。预览 URL 遵循格式 `{org}-preview-{id}.docs.buildwithfern.com`,因此使用相同的 `--id` 重新运行会就地更新现有预览,而不是创建新预览。 + + ```bash + fern generate --docs --preview --id my-feature + # -> https://your-org-preview-my-feature.docs.buildwithfern.com + ``` + + 这在您希望每个拉取请求有一个预览 URL 的 CI 工作流程中很有用。请参阅[预览更改](/learn/docs/preview-publish/preview-changes#preview-links)了解详细信息。 + + ### force + + 当重用已存在的 `--id` 时,Fern 会提示您确认覆盖。使用 `--force` 跳过确认。这在 GitHub Actions 中会自动检测到,但在其他 CI 环境(如 Azure Pipelines)中需要。 + + ```bash + fern generate --docs --preview --id my-feature --force + ``` + + + + + + 使用 `fern docs preview list` 列出您组织的所有预览部署。 + + + ```bash + fern docs preview list [--limit ] [--page ] + ``` + + + ### limit + + 使用 `--limit` 指定每页显示的预览部署数量。 + + ```bash + fern docs preview list --limit 20 + ``` + + ### page + + 使用 `--page` 指定要显示结果的页面。 + + ```bash + fern docs preview list --page 2 + ``` + + + + + + 使用 `fern docs preview delete` 删除使用 `fern generate --docs --preview` 生成的预览部署。`` 参数是要删除的完整预览 URL。 + + + ```bash + fern docs preview delete + ``` + + + + + + + 使用 `fern docs dev` 运行本地开发服务器来预览您的文档。 + + + ```bash + fern docs dev [--port ] + ``` + + + + 在 Windows 上,`fern docs dev` 需要启用[长路径支持](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation#enable-long-paths-in-windows-10-version-1607-and-later)。 + + 要启用长路径支持,请在提升的 PowerShell 提示符中运行以下命令,然后重启您的终端: + + ```powershell + New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1 -PropertyType DWORD -Force + ``` + + 如果您无法启用长路径支持,请使用 [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) 在 Linux 环境中运行 `fern docs dev`。 + + + ### port + + 使用 `--port ` 指定文档预览运行的端口。 + + ```bash + fern docs dev --port 57908 + ``` + + + + + + 使用 `fern docs md check` 验证导航配置中引用的所有文档页面的 MDX 语法,包括 `docs.yml`、版本配置文件和产品特定的 YAML 文件。 + + + ```bash + fern docs md check + ``` + + + 该命令解析每个 MDX 文件并报告语法错误,包含文件路径和行:列号。该命令考虑 frontmatter 偏移量,因此报告的行号与文件中的实际行对应。 + + ```plaintext + fern/pages/quickstart.mdx:12:5 + Unexpected closing tag, expected corresponding closing tag for `` + + fern/pages/guide.mdx:45:1 + Expected a closing tag for `` before the end of `paragraph` + ``` + + 当所有文件都有效时,您将看到成功消息: + + ```plaintext + ✓ All 42 MDX files are valid + ``` + + + + + Beta + + 使用 `fern docs diff` 在预览部署和生产文档之间生成视觉差异。此命令旨在用于 [GitHub Actions](https://github.com/fern-api/docs/blob/main/.github/workflows/preview-docs.yml)。它捕获两个版本的截图并创建并排比较图像。 + + + ```bash + fern docs diff [--output ] + ``` + + + 传递来自 `fern generate --docs --preview` 的预览 URL 和一个或多个 MDX 文件路径。差异图像默认保存到 `.fern/diff`。 + + ```bash + fern docs diff acme-preview-abc123.docs.buildwithfern.com fern/pages/intro.mdx fern/pages/quickstart.mdx + ``` + + ### output + + 使用 `--output` 为差异图像指定自定义目录。 + + ```bash + fern docs diff acme-preview-abc123.docs.buildwithfern.com fern/pages/intro.mdx --output ./my-diffs + ``` + + + + + + 使用 `fern upgrade` 将 [`fern.config.json`](/learn/sdks/overview/project-structure#fernconfigjson) 中的编译器版本升级到最新版本。它还将 `generators.yml` 中的生成器升级到其最低兼容版本。 + + + ```bash + fern upgrade + ``` + + + + + + + 使用 `fern login` 通过 GitHub 或 Google 登录 Fern CLI。登录允许您加入 GitHub 组织、获得权限并为项目做出贡献。 + + + ```bash + fern login + fern login --device-code + fern login --email + ``` + + + 默认情况下,`fern login` 打开浏览器进行 GitHub、Google 或 Postman 身份验证。有两种替代流程可用: + + ### device-code + + 在无法自动打开浏览器的环境中(例如 SSH 会话或容器),使用 `--device-code` 通过设备代码授权登录。 + + ```bash + fern login --device-code + ``` + + ### email + + 使用 `--email` 通过企业 SSO 登录。传递与您组织的 SSO 提供商关联的电子邮件地址。 + + ```bash + fern login --email user@example.com + ``` + + + 要启用 CI/CD,请使用 [`fern token`](/learn/cli-api/cli-reference/commands#fern-token)。 + + + + + + + 使用 `fern logout` 从 Fern CLI 注销。这将清除您的身份验证凭据并撤销对您的 GitHub 组织和权限的访问。 + + + ```bash + fern logout + ``` + + + 注销后,您需要再次运行 [`fern login`](#fern-login) 来访问受保护的功能。 + + + + + + + + 请参阅[发布您的文档](/learn/docs/preview-publish/publishing-your-docs#usage-in-github-actions),了解在自动化发布工作流程中使用此令牌的说明。 + + + + + + 使用 `fern write-definition` 将您的 OpenAPI 规范转换为 Fern Definition。您必须有一个包含 `.json` 或 `.yaml` 格式的 OpenAPI 规范文件的 `fern/openapi/` 文件夹。 + + + ```bash + fern write-definition [--api ] + ``` + + + 运行此命令时,会在 `fern/` 内创建一个名为 `.definition/` 的新文件夹。 + + + + + + + + + + + + + + + + + 如果您没有看到 `.definition/` 文件夹,请使用适当的命令或配置来查看隐藏文件夹(在 `bash` 和 `zsh` 中使用 `ls -a`)。 + + + 如果您的 `fern/` 文件夹同时包含 `openapi/` 和 `definition/` 文件夹,Fern 默认读取您的 OpenAPI 规范。要使用您的 Fern Definition 作为输入,您必须: + - 将 `.definition/` 文件夹重命名为 `definition/`。 + - 删除或重命名 `openapi/` 文件夹。例如,您可以将其重命名为 `.openapi/`。 + + ### api + + 如果您在 `fern/apis/` 文件夹中定义了多个 API,请使用 `--api` 指定要为其编写定义的 API。 + + ```bash + fern write-definition --api public-api + ``` + + + + + 使用 `fern write-overrides` 生成基本的 OpenAPI 覆盖文件。覆盖文件允许对 API 规范进行可逆修改,包括为 Fern Docs 中的代码片段添加请求和响应示例。 + + + ```bash + fern write-overrides [--api ] [--exclude-models] + ``` + + + 运行此命令时,会在 `fern/openapi/` 内创建一个名为 `openapi-overrides.yml` 的新文件。 + + + + + + + + + + + + + ### api + + 如果定义了多个 API,请使用 `--api` 指定要对其运行命令的 API。 + + ```bash + fern write-overrides --api public-api + ``` + + ### exclude-models + + 使用 `--exclude-models` 在生成初始覆盖时存根模型(除了端点)。 + + ```bash + fern write-overrides --exclude-models + ``` + + + + + 使用 `fern generator upgrade` 将 `generators.yml` 中的所有生成器更新到其最新版本。 + + + 这与更新 Fern CLI 版本的 `fern upgrade` 不同。使用这两个命令来保持整个 Fern 工具链的最新状态。 + + + + ```bash + fern generator upgrade [--list] [--generator ] [--group ] [--include-major] + ``` + + + 此命令将: + - 检查 `generators.yml` 中指定的所有生成器的更新 + - 将生成器版本更新到其最新兼容版本 + - 保持与当前 Fern 编译器版本的兼容性 + + 当有可用更新时,您可能会看到: + + ```plaintext + ┌───────────────────────────────────────────────────────────────────────────────────┐ + │ │ + │ Upgrades available │ + │ │ + │ │ + │ C# SDK (API: openapi, Group: csharp-sdk) 1.9.11 → 1.9.15 │ + │ Java SDK (API: openapi, Group: java-sdk) 2.2.0 → 2.11.3 │ + │ Python SDK (API: openapi, Group: python-sdk) 4.3.10 → 4.3.11 │ + │ │ + │ Run fern generator upgrade to upgrade your generators. │ + │ Run fern generator upgrade --list to see the full list of generator upgrades │ + │ available. │ + │ │ + └───────────────────────────────────────────────────────────────────────────────────┘ + ``` + + ### list + + 使用 `--list` 查看可用生成器升级的完整列表。 + + ```bash + fern generator upgrade --list + ``` + + ### generator + + 使用 `--generator` 指定要升级的特定生成器类型。 + + ```bash + fern generator upgrade --generator fernapi/fern-typescript-sdk + fern generator upgrade --generator fernapi/fern-python-sdk + ``` + + ### group + + 使用 `--group` 升级 `generators.yml` 中特定组内的生成器。如果未指定,将升级指定类型的所有生成器。 + + ```bash + fern generator upgrade --group public + ``` + + ### include-major + + ```bash + fern generator upgrade --include-major + ``` + + 使用 `--include-major` 包括主要版本升级。默认跳过主要版本以防止破坏性更改。 + + + + 从 `generators.yml` 中指定的 `origin` 拉取最新的 OpenAPI 规范并更新本地规范。或者,您可以[通过设置 GitHub Action 来自动化此过程](/api-definitions/openapi/sync-your-open-api-specification)。 + + + ```bash + fern api update [--api ] + ``` + + + ### api + + 如果 `generators.yml` 中有多个定义了 `origin` 的规范,请使用 `--api` 指定要更新的 API。如果您不指定 API,所有有 `origin` 的 OpenAPI 规范都将被更新。 + + + ```bash + fern api update --api public-api + ``` + + + + + + 使用 `fern api enrich` 将 [AI 生成的示例](/learn/docs/ai-features/ai-examples)或手动创作的 [`x-fern-examples`](/learn/api-definitions/openapi/extensions/request-response-examples) 转换为任何兼容 OpenAPI 的工具都可以使用的可移植 OpenAPI 示例。 + + + ```bash + fern api enrich -f -o + ``` + + + 该命令将覆盖文件中的示例合并到原生 OpenAPI `example` 字段中,并从输出中剥离 `x-fern-examples` 键。当端点有多个示例时,每个示例都作为命名条目存储在复数 `examples` 字段下。 + + 该命令需要两个标志: + + - `-f`(或 `--file`)— 包含 `x-fern-examples` 的覆盖文件(例如,`ai_examples_override.yml`)。 + - `-o`(或 `--output`)— 丰富输出文件的路径。支持 `.yml` 和 `.json` 扩展名。 + + ```bash + # 输出为 YAML + fern api enrich openapi.yml -f overrides.yml -o enriched-openapi.yml + + # 输出为 JSON + fern api enrich openapi.yml -f overrides.yml -o enriched-openapi.json + ``` + + 每个 `x-fern-examples` 字段都映射到其标准 OpenAPI 位置: + + - `path-parameters` → `parameters[].example`(其中 `in: path`) + - `query-parameters` → `parameters[].example`(其中 `in: query`) + - `headers` → `parameters[].example`(其中 `in: header`) + - `request.body` → `requestBody.content.*.example` + - `response.body` → `responses..content.*.example` + + + + + + 使用 `fern docs theme export` 将 `docs.yml` 中的[主题兼容字段](/learn/docs/customization/global-themes)提取到独立目录中。导出的 `theme.yml` 及其资源然后可以通过 `fern docs theme upload` 上传。 + + + ```bash + fern docs theme export [--output ] + ``` + + + 默认情况下,文件写入 `./fern/theme/`。 + + ### output + + 使用 `--output` 为导出的主题指定自定义目录。 + + ```bash + fern docs theme export --output ./my-theme + ``` + + + + + + 使用 `fern docs theme upload` 将[主题](/learn/docs/customization/global-themes)上传到 Fern 的注册表。该命令从 `./fern/theme/` 读取 `theme.yml` 并连同任何引用的文件资源一起上传。 + + + ```bash + fern docs theme upload [--name ] [--org ] + ``` + + + ### name + + 使用 `--name` 设置主题名称。默认为 `default`。 + + ```bash + fern docs theme upload --name my-theme + ``` + + ### org + + 使用 `--org` 覆盖来自 `fern.config.json` 的组织 ID。 + + ```bash + fern docs theme upload --org my-org + ``` + + + + + + 使用 `fern docs theme list` 列出为您的组织上传的所有[主题](/learn/docs/customization/global-themes)。 + + + ```bash + fern docs theme list [--json] [--org ] + ``` + + + 默认情况下,每行输出一个主题名称。 + + ### json + + 使用 `--json` 将完整列表输出为 JSON 数组,包括 `updatedAt` 时间戳。 + + ```bash + fern docs theme list --json + ``` + + ### org + + 使用 `--org` 覆盖来自 `fern.config.json` 的组织 ID。 + + ```bash + fern docs theme list --org my-org + ``` + + + \ No newline at end of file diff --git a/fern/translations/zh/products/cli-api-reference/pages/global-options.mdx b/fern/translations/zh/products/cli-api-reference/pages/global-options.mdx new file mode 100644 index 000000000..c93f9be77 --- /dev/null +++ b/fern/translations/zh/products/cli-api-reference/pages/global-options.mdx @@ -0,0 +1,66 @@ +--- +title: '全局选项' +description: 'Fern CLI 的全局选项' +subtitle: '了解 Fern CLI 全局选项。' +sidebar-title: '全局选项' +--- + + +这些选项适用于任何 Fern CLI 命令,用于控制日志记录和获取帮助。 + +| 选项 | 描述 | 示例 | +|--------|-------------|---------| +| [`--help`](#help) | 显示命令帮助和选项 | `fern --help` | +| [`--log-level`](#log-level) | 设置日志详细程度 | `fern generate --log-level debug` | + + +使用 `--log-level debug` 在故障排除时查看详细输出。 + + +## help + +使用 `--help` 选项与任何 Fern CLI 命令一起查看说明和可用选项。 + + +```bash maxLines=10 title="fern add --help" +fern add --help +fern add + +Add a code generator to generators.yml + +Positionals: + generator [string] [required] + +Options: + --help Show help [boolean] + --log-level [choices: "debug", "info", "warn", "error"] [default: "info"] + --api Only run the command on the provided API [string] + --group Add the generator to the specified group [string] +``` + +```bash maxLines=10 title="fern write-definition --help" +fern write-definition --help +fern write-definition + +Write underlying Fern Definition for OpenAPI Specifications and API Dependencies. + +Options: + --help Show help [boolean] + --log-level [choices: "debug", "info", "warn", "error"] [default: "info"] + --api Only run the command on the provided API [string] +``` + + +## log-level + +使用 `--log-level` 选项设置 Fern 日志输出的详细程度。默认级别为 `info`。 + +可用级别(从详细到简略): +- `debug`:调试消息、信息消息、警告和错误 +- `info`:信息消息、警告和错误 +- `warn`:仅警告和错误 +- `error`:仅错误消息 + +```bash +fern generate --log-level debug +``` \ No newline at end of file diff --git a/fern/translations/zh/products/dashboard/pages/changelog/2026-01-05.mdx b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-05.mdx new file mode 100644 index 000000000..92cb3475a --- /dev/null +++ b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-05.mdx @@ -0,0 +1,14 @@ +--- +tags: ["seo"] +--- + +## 链接检查器 + +Fern Dashboard 现在包含一个链接检查器,可以扫描您已发布的文档中的损坏链接,验证内部链接(指向文档内其他页面的链接)和外部链接(指向外部网站的链接)。 + +要检查损坏链接,请打开 [Fern Dashboard](https://dashboard.buildwithfern.com),导航到**链接检查器**选项卡,并选择要扫描的域名。检查器会显示找到的任何损坏(404)或被阻止(403)的链接,以及源页面和链接类型。 + + + + Dashboard Link Checker tool + \ No newline at end of file diff --git a/fern/translations/zh/products/dashboard/pages/changelog/2026-01-07.mdx b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-07.mdx new file mode 100644 index 000000000..7425656a7 --- /dev/null +++ b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-07.mdx @@ -0,0 +1,9 @@ +--- +tags: ["customization"] +--- + +## 在控制面板中设置自定义域名 + +您现在可以直接在 [Fern Dashboard](https://dashboard.buildwithfern.com/) 中为您的文档网站配置自定义域名。控制面板会根据您的域名类型自动显示需要添加到域名注册商的 DNS 记录。 + + \ No newline at end of file diff --git a/fern/translations/zh/products/dashboard/pages/changelog/2026-01-20.mdx b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-20.mdx new file mode 100644 index 000000000..413a95e37 --- /dev/null +++ b/fern/translations/zh/products/dashboard/pages/changelog/2026-01-20.mdx @@ -0,0 +1,9 @@ +--- +tags: ["security"] +--- + +## 用户权限 + +您现在可以为组织中的团队成员分配角色,以控制谁可以发布到生产环境和管理组织设置。可以选择管理员、编辑者或查看者角色,并可选择限制对特定文档网站的访问。 + + \ No newline at end of file diff --git a/fern/translations/zh/products/dashboard/pages/changelog/2026-02-13.mdx b/fern/translations/zh/products/dashboard/pages/changelog/2026-02-13.mdx new file mode 100644 index 000000000..bff965c35 --- /dev/null +++ b/fern/translations/zh/products/dashboard/pages/changelog/2026-02-13.mdx @@ -0,0 +1,21 @@ +--- +tags: ["pdf-export"] +--- + +## PDF 导出 + +您现在可以直接从 Fern Dashboard 将整个文档网站导出为 PDF。 + +每个导出包括: +- 带有可自定义标题和副标题的封面页 +- 带有可点击链接的自动生成目录 +- 所有内容页面 +- 带有页码占位符的可选页眉和页脚 + + + +要开始使用,请在 [Dashboard](https://dashboard.buildwithfern.com/) 中转到**概览** > **⋮** > **导出为 PDF**。 + + + +## 身份验证 + +API 探索器支持在您的 OpenAPI 规范或 `generators.yml` 中配置的[所有身份验证方案](/learn/api-definitions/openapi/authentication),包括多种身份验证方案。当有多种方案可用时,API 探索器会自动在下拉菜单中显示它们,允许用户在发送请求之前选择并配置其首选的身份验证方法。 + +一旦用户设置了身份验证凭据,他们的凭据将在整个探索会话期间保持不变。 + +
+ + +身份验证凭据仅使用 cookie 存储在客户端。不会收集或存储任何敏感的用户信息。 + + +要为已登录用户自动填充 API 密钥,请参阅 [API 密钥注入](/learn/docs/authentication/features/api-key-injection)。 + +## 多环境 + +当在 [OpenAPI](/learn/api-definitions/openapi/extensions/server-names-and-url-templating) 或 [Fern Definition](/learn/api-definitions/ferndef/api-yml/environments) 中配置了多个服务器 URL 时,用户可以从 API 探索器的下拉菜单中在不同环境(例如,生产环境和沙盒环境)之间切换。所选环境在他们在页面之间导航时保持不变。 + +
+ +用户还可以双击服务器 URL 手动编辑它,允许快速测试自定义环境或端点。 + +这是配置了多个服务器名称的 [Flagright 文档网站](https://docs.flagright.com/framl-api/api-reference/api-reference/transactions/get) 示例。 + + + + + ```yaml + openapi: 3.0.0 + servers: + - url: https://sandbox.api.flagright.com + x-fern-server-name: Sandbox API server (eu-1) + - url: https://sandbox-asia-1.api.flagright.com + x-fern-server-name: Sandbox API server (asia-1) + ``` + + + + + ```yaml + environments: + Sandbox API server (eu-1): https://sandbox.api.flagright.com + Sandbox API server (asia-1): https://sandbox-asia-1.api.flagright.com + ``` + + + + +## WebSocket Playground + +对于支持 WebSocket 连接的 API,API 探索器包含一个 **WebSocket** 特定的 Playground。WebSocket Playground 还允许用户与 API 建立连接,并实时发送/接收消息。 + +
+ +## 控制 API 探索器可用性 + +对于 OpenAPI 规范,API 探索器默认为所有端点启用。您可以使用 [`x-fern-explorer`](/learn/api-definitions/openapi/extensions/api-explorer-control) 扩展全局或按端点禁用它。这通常用于禁用破坏性操作、支付处理或仅限管理员端点的探索器。 \ No newline at end of file diff --git a/fern/translations/zh/products/docs/pages/api-references/api-ref-content.mdx b/fern/translations/zh/products/docs/pages/api-references/api-ref-content.mdx new file mode 100644 index 000000000..dc7502a48 --- /dev/null +++ b/fern/translations/zh/products/docs/pages/api-references/api-ref-content.mdx @@ -0,0 +1,127 @@ +--- +title: 在 API Reference 中编写 Markdown 内容 +description: 在 API 文档中编写丰富的 Markdown 内容。为端点添加描述,创建摘要页面,并自定义您的 API Reference 布局。 +sidebar-title: 在 API 参考中编写 Markdown 内容 +--- + + +Fern Docs 允许您在 API Reference 文档中编写 Markdown 内容。此功能对于为 API 端点提供额外的上下文、示例或说明非常有用。 + +## 为端点添加 Markdown 内容 + +您可以使用 `description` 字段(OpenAPI)或 `docs` 字段(Fern Definition)在 API 定义中包含 Markdown 内容。这包括标注、代码块和[其他组件](/learn/docs/writing-content/components/overview)。 + +您还可以使用 `