@chaimp-org/feedback-ado (英语)
这个 ChAImp SaaS 通用反馈引擎 - 每个产品一个实施 安装将用户的β反馈变成 Azure DevOps 工作项目, (中文(简体) ), 由产品Entra认证 应用程序注册 结束 韦塞尔 OIDC 联邦 (没有秘密存放在任何地方).
为什么
每个产品都有(或需要)自己的"feedback ~ tracker"的复制品. 这是 单一、共享、经核实的执行:Bug/User-Story拆分、Markdown机构、, 标记、优先支持的路由,以及一个从未有过的弹性最佳呼叫 抛出(产品 DB 行保持真实的来源).
安装
pnpm add @chaimp-org/feedback-ado
使用量
在服务器路由中, 构建工作项目, 并将其存档
下级 waitUntil (一个赤裸的火与忘却的许诺在服务器无时被冻结)
在 ADO 创建完成前返回响应 :
import { waitUntil } from "@vercel/functions";
import { adoApiEnabled, buildFeedbackWorkItem, createAdoWorkItem } from "@chaimp-org/feedback-ado";
// ... after persisting the feedback row (source of truth) ...
if (adoApiEnabled()) {
waitUntil(
createAdoWorkItem(
buildFeedbackWorkItem({
category: parsed.category, // "bug" → Bug, else User Story
message: parsed.message,
orgName: ctx.org.name,
orgSlug: ctx.org.slug,
userEmail: ctx.userEmail,
plan: ctx.org.plan,
page: parsed.page,
priority: isPro, // ⚡ + Priority 1 + priority-support tag
envLabel: process.env.NEXT_PUBLIC_ENV_LABEL,
}),
).catch(() => {}),
);
}
buildFeedbackWorkItem 是可选的——呼叫 createAdoWorkItem(item) 直接,如果
您想要自己塑造工作项目.
先通过ATFS(0.5.0)
submitFeedback 将反馈发送给门户的 ATFS 接收器
(单位:千美元)POST /api/atfs/intake) (中文(简体) ). 传送门对它进行分解并归档. 当传送门
肯定没有接受反馈, submitFeedback 直接在 ADO 中存档, 如
在0.4中,它从不扔.
import { waitUntil } from "@vercel/functions";
import { submitFeedback } from "@chaimp-org/feedback-ado";
waitUntil(
submitFeedback(
{
category: parsed.category,
message: parsed.message,
product: "cartoncrew",
orgId: ctx.org.id, // the product's org id (the portal's tenant external_ref)
orgName: ctx.org.name,
orgSlug: ctx.org.slug,
userEmail: ctx.userEmail,
page: parsed.page,
},
{ idempotencyKey: feedbackRow.id }, // your stored row id: a re-submit is then safe too
).then((r) => {
if (!r.ok) console.warn("[feedback]", r.via, r.reason); // r.reason never holds a token
}),
);
结果:
via | ok | 含义 |
|---|---|---|
atfs | 真实 | 门户保存了下来. 没有直接归档 |
ado | 真实 | 直接存档 . reason 说明为什么( 门户没有配置, ATFS 关闭, 无法到达, 拒绝) |
ado | 虚假 | 门户网站没有接收, 而直接归档也失败了 |
uncertain | 虚假 | 门户 可能 已贮藏了它,没有再试了。 没有直接提交,所以不能提交两次。 保持行并再次提交 稍后用同样的 idempotencyKey |
无二相相相法相. 直接归档只在门户时发生 绝对没有存储提交文件。 这意味着其中之一发生了:
- 门户没有配置;
- 传送门已回复
accepted: false· ; - 在未知结果出现之前, 连接从未打开或传送门返回到 4xx (英语).
503是拒绝 只有当它的身体是 摄取者自己的JSON拒绝
(单位:千美元)accepted: false; 平台上的光线503或代理未知(0.7.0).
请求出道后的超时时间未知,重设和500,502或
504. 正在重新审理一个未知结果 不变 一能之钥. 门户保存
每个产品每个密钥一个项目,所以重试返回所储存的项目。 一旦有任何尝试
但不知道,只有门户的明确答复才能解决结果。 直接
倒置文件被标记 atfs-fallback。 。 。 .
直接备案为一等功(0.7.0). 直接归档带有标签
fbk-<hash> (单位:千美元)idemKeyTag(key): SHA-256中最早的32个十六进制字符
单能键)。 在它创建之前,它寻找一个带有由WIQL标记的项目. 如果一个
,该 id 以 duplicate: true 并且没有存档。 如果检查
失败, 没有文件, 结果就是 ok: false; 稍后再次提交相同的文件
键.
选项 : idempotencyKey (默认:随机UUID), portal (覆盖地洞;通过)
null 跳过传送门, retries (违约2), timeoutMs (默认15000,以上)
undici的10 s连接超时, retryDelayMs (违约500起), surface,加上每个
createAdoWorkItemId 选项.
配置(环境)
每个产品都有相同的可变名称;将其设置在产品的Vercel中
项目(和 .env.secrets.local 当地PAT倒计时:
| 瓦尔 | 目的 |
|---|---|
ADO_ORG | 组织,例如:. chaimporg |
ADO_PROJECT | 项目,例如:. One |
ADO_AREA_PATH | 按产品需要,比如说. One\SaaS\<slug> (缺省项目) |
ADO_TENANT_ID | Entra 目录( 租户) id —— 首选的认证 |
ADO_CLIENT_ID | Entra 应用程序( 客户端) id —— 首选认证 |
ADO_PAT | 仅本地倒计时; 当 ADO_TENANT_ID编号 :ADO_CLIENT_ID 未设定 |
ATFS_INTAKE_URL | 0.5.0: 门户基础 URL, https(例如) https://portal.chaimp.org) (中文(简体) ). 未设置仅意味着直接归档 |
ATFS_INTAKE_TOKEN | 0.5.0:本产品摄取代币. 门户持有与 ATFS_INTAKE_TOKEN_<SLUG>这是一个秘密,所以只保留在Vercel env |
首选认证——Vercel OIDC联合会(保密)
- 启用 OIDC 联合会 在Vercel项目.
- 在应用程序注册时,加一个 联邦证书: 发行者
https://oidc.vercel.com/<team>,主题owner:<team>:project:<vercel-project>:environment:<env>,观众api://AzureADTokenExchange。 。 。 . - 将服务主机添加到 ADO ORG 中 贡献 在区域路径.
包交换所注入的 VERCEL_OIDC_TOKEN azure DevOps 访问权限
符号( 资源) 499b84ac-…/.default)通过 @azure/identity是因为
ClientAssertionCredential。没有隐藏秘密.
冲浪(产品与舞台)
发动机和有效载荷共享; 如何在UI中显示反馈是唯一
不同的东西 在产品之间(复制/花粉、类别、放置),因此
配置 不是代码 声明a FeedbackSurface 每个产品:
const surface: FeedbackSurface = {
product: "knowledgeretention",
stage: "beta", // "ga" once it graduates — no code change
title: "Beta feedback", // GA products: "Send feedback" / "Help & feedback"
categories: [
{ value: "bug", label: "Report a bug", isBug: true },
{ value: "idea", label: "Suggest something" },
{ value: "general", label: "General" },
],
placement: "beta-badge", // GA: "help-menu" | "footer" | "nav"
};
createAdoWorkItem(buildFeedbackWorkItem({ category, message, /* … */ }, surface));
product + 键 stage 流入工作项目 标记 (单位:千美元)beta编号 :ga,产品弹)和
机体,因此分解区分 KR β bug 从卡通Crew GA bug(在
区域路径)。 "Beta"不是特例——这是 stage。 。 。 .
API 密码
submitFeedback(input, opts?): Promise<SubmitFeedbackResult>(0.5.0)先将反馈发送给门户的ATFS摄取. 只有当门户绝对没有储存时,才直接归档(见上文).idemKeyTag(key): string(0.7.0)——带有一能键的ADO标记.atfsIntakeConfig(): AtfsPortalConfig | null(0.5.0)改为:ATFS_INTAKE_URL+ 键ATFS_INTAKE_TOKEN。 。 。 .adoApiEnabled(): boolean– 足以显示配置文件( 如跳过; DB 是真相的来源) .createAdoWorkItem(item): Promise<boolean>——文件一个工作项目;从不丢出.createAdoWorkItemId(item, opts?): Promise<number | null>相同,但还原新的 工作项目 id (在任何失败时取消).opts.idempotencyKey(0.7.0)使它具有以下能力: 已标记的项目idemKeyTag(key)返回,而不是提交第二个.opts.identity(单位:千美元){ tenantId, clientId }(d) 认证 作为不同的应用程序注册(例如,最不优惠的备案人),opts.parentId添加 父为Hierarchy-Reverse关系(从未作为字段),opts.areaPath覆盖ADO_AREA_PATH, (中文(简体) ),opts.iterationPath套装System.IterationPath, (中文(简体) ),opts.assignedTo套装System.AssignedTo(显示名称),opts.onFailure(reason)被告诉一个简短的无秘密 理由,当没有提交,opts.fetchImpl为测试注入运输工具。 全体 选项是可选的;省略它们会保持先前的行为。 自0.6.0 a起 错误 将Markdown的正文放在两个步骤中System.Description(董事会要求 描述除测试外的每个项目;用户故事不变.buildFeedbackWorkItem(input, surface?): WorkItemInput——共享房屋风格(标题,Markdown body,标签,优先级);可选surface驱动 Bug/Story映射并携带出品+级. 自0.8.0起,身体随标题而打开**Feedback** (the sender's own words, as typed; not verified):和消息作为块引用(每行前缀)>每一分行形式都造出一条新线,<逃脱了,那么---和元数据列表。 键入- **Env:** production线条保留在引文内,所以唯一未引用的元数据行是软件包自己的. 每个元数据值都保留在一条行上,其Markdown字符逃出. 标题是信息中最简单的第一道非空行,从来不是被引用的正文. 可选title输入( 平面文本、 被产品擦除、 从未逃脱; 控制字符和换行符成为空格; 最多120个字符) give[category] slug: <title>,然后作为subject曲吉主题. 部署命令: 门户网站必须在产品发送前运行0.166.0或更晚title; 一个旧的门户拒绝未知subject(400个)和submitFeedback然后直接在 ADO 中归档项目 (atfs-fallback)),所以它从未丢失或两次存档,但跳过ATFS分录. 自0.9.0起(AB#16958),每一条被引用的行也是完全被逃脱的,因此消息从字面上使:&财务报告和审定财务报表<成为实体, Markdown 字符 (\ ` * _ [ ] ( ) ! > ~ | # $ @)是反斜-被逃脱的,a.或者说:在 URL 中,域名或电子邮件地址被跳出,因此没有自动链接,行首的列表,标题或设置ext标记被跳出. 没有键入链接、图像、 强调、 代码、 标题、 列表、 切换、 实体或 HTML 生成, 发件人选择的 URL 中没有任何负载 。 元数据值也逃脱&财务报告和审定财务报表~。 。 。 . 发送纯文本 : 文本一个产品已脱逃(\-, (中文(简体) ),<现在显示它的后鞭和实体。 消息衍生标题最多保持了70个代码点,因此切口处的emoji从不被分拆. ATFS收录路径不变:它以纯文本的形式接收消息.quoteUntrusted(text): string——上面使用的被逃出块状引文,用于构建自己身体的产品(0.8.0;自0.9.0起被完全逃出.resolveType(category, surface?): WorkItemType—— Bug vs 类用户故事( 使用表面)categories编号 :isBug; 默认为category === "bug") (中文(简体) ).- 类型 :
FeedbackSurface, (中文(简体) ),FeedbackCategory, (中文(简体) ),FeedbackStage, (中文(简体) ),FeedbackInput, (中文(简体) ),WorkItemInput, (中文(简体) ),WorkItemType。 。 。 .