Files
superwork/vscode/src/webhook/eventParser.ts
T

178 lines
4.8 KiB
TypeScript

export interface PrWebhookEvent {
kind: 'pr'
issueNumber: number | undefined
action: string
pr: string
branch: string
htmlUrl: string
title: string
body: string
raw: unknown
}
export interface IssueWebhookEvent {
kind: 'issue'
action: string
issueNumber: number
title: string
body: string
htmlUrl: string
raw: unknown
}
export interface IssueCommentWebhookEvent {
kind: 'issue_comment'
action: string
issueNumber: number
prNumber: number | undefined
commentBody: string
commentAuthor: string
commentHtmlUrl: string
raw: unknown
}
export interface PushWebhookEvent {
kind: 'push'
branch: string
raw: unknown
}
export type WebhookEvent = PrWebhookEvent | IssueWebhookEvent | IssueCommentWebhookEvent | PushWebhookEvent
export function parseEvent(
eventHeader: string,
issueNumber: number | undefined,
raw: unknown,
): WebhookEvent | null {
if (!raw || typeof raw !== 'object')
return null
const obj = raw as {
action?: unknown
pull_request?: unknown
issue?: unknown
comment?: unknown
ref?: unknown
sender?: unknown
}
if (eventHeader === 'push') {
// gitea push 事件无 `action`,顶层带 `ref: "refs/heads/<branch>"`。
if (typeof obj.ref !== 'string')
return null
const branch = obj.ref.replace(/^refs\/heads\//, '')
if (!branch)
return null
return { kind: 'push', branch, raw }
}
if (typeof obj.action !== 'string')
return null
if (eventHeader === 'issue_comment') {
const issue = obj.issue
const comment = obj.comment
if (!issue || typeof issue !== 'object')
return null
if (!comment || typeof comment !== 'object')
return null
const issueObj = issue as {
number?: unknown
pull_request?: unknown
}
const commentObj = comment as {
body?: unknown
html_url?: unknown
user?: unknown
}
const num = typeof issueObj.number === 'number' ? issueObj.number : Number(issueObj.number)
if (!Number.isFinite(num))
return null
// gitea sets issue.pull_request (non-null object) when the issue is a PR.
const prNumber = issueObj.pull_request && typeof issueObj.pull_request === 'object'
? num
: undefined
const commentBody = typeof commentObj.body === 'string' ? commentObj.body : ''
const commentHtmlUrl = typeof commentObj.html_url === 'string' ? commentObj.html_url : ''
const commentUser = commentObj.user as { login?: unknown, username?: unknown } | undefined
const sender = obj.sender as { login?: unknown, username?: unknown } | undefined
const commentAuthor = (typeof commentUser?.login === 'string' && commentUser.login)
|| (typeof commentUser?.username === 'string' && commentUser.username)
|| (typeof sender?.login === 'string' && sender.login)
|| (typeof sender?.username === 'string' && sender.username)
|| ''
return {
kind: 'issue_comment',
action: obj.action,
issueNumber: num,
prNumber,
commentAuthor,
commentBody,
commentHtmlUrl,
raw,
}
}
if (eventHeader === 'issues') {
const issue = obj.issue
if (!issue || typeof issue !== 'object')
return null
const issueObj = issue as {
number?: unknown
html_url?: unknown
title?: unknown
body?: unknown
}
const num = typeof issueObj.number === 'number' ? issueObj.number : Number(issueObj.number)
if (!Number.isFinite(num))
return null
const htmlUrl = typeof issueObj.html_url === 'string' ? issueObj.html_url : ''
const title = typeof issueObj.title === 'string' ? issueObj.title : ''
const body = typeof issueObj.body === 'string' ? issueObj.body : ''
return {
kind: 'issue',
action: obj.action,
issueNumber: num,
title,
body,
htmlUrl,
raw,
}
}
// Default path: pull_request payload (preserves legacy behaviour for any
// event header value, since gitea historically may not set the header).
const pr = obj.pull_request
if (!pr || typeof pr !== 'object')
return null
const prObj = pr as {
number?: unknown
html_url?: unknown
head?: unknown
title?: unknown
body?: unknown
}
const num = typeof prObj.number === 'number' ? prObj.number : Number(prObj.number)
if (!Number.isFinite(num))
return null
const htmlUrl = typeof prObj.html_url === 'string' ? prObj.html_url : ''
const head = prObj.head as { ref?: unknown } | undefined
const branch = head && typeof head.ref === 'string' ? head.ref : ''
const title = typeof prObj.title === 'string' ? prObj.title : ''
const body = typeof prObj.body === 'string' ? prObj.body : ''
if (!branch || !htmlUrl)
return null
return {
kind: 'pr',
issueNumber,
action: obj.action,
pr: String(num),
branch,
htmlUrl,
title,
body,
raw,
}
}