import {
asRecord,
booleanField,
isRecord,
originErrorMessage,
stringField,
} from './http.ts'
import { originRequest } from './request.ts'
import {
type OriginAccountInput,
type OriginMutationInput,
type OriginPullSummary,
} from './types.ts'
/**
* List pull requests on an Origin repository.
*/
export async function listOriginPulls(
params: OriginAccountInput & {
ownerSlug: string
repoName: string
},
): Promise<{ items: Array<OriginPullSummary> }> {
const ownerSlug = params.ownerSlug.trim()
const repoName = params.repoName.trim()
if (!ownerSlug || !repoName) {
throw new Error('ownerSlug and repoName are required.')
}
const result = await originRequest({
...params,
path: `/repos/${encodeURIComponent(ownerSlug)}/${encodeURIComponent(repoName)}/pulls`,
auth: 'installation',
})
if (!result.ok) {
throw new Error(
`Failed to list Origin pull requests: ${originErrorMessage(result.body, result.status)}`,
)
}
const payload = isRecord(result.body) ? result.body : {}
const raw = Array.isArray(payload.pulls)
? payload.pulls
: Array.isArray(payload.items)
? payload.items
: Array.isArray(result.body)
? result.body
: []
return {
items: raw.map((item) => slimPull(asRecord(item))),
}
}
/**
* Fetch one Origin pull request.
*/
export async function getOriginPull(
params: OriginAccountInput & {
ownerSlug: string
repoName: string
pullNumber: string | number
},
): Promise<OriginPullSummary> {
const ownerSlug = params.ownerSlug.trim()
const repoName = params.repoName.trim()
const pullNumber = String(params.pullNumber).trim()
if (!ownerSlug || !repoName || !pullNumber) {
throw new Error('ownerSlug, repoName, and pullNumber are required.')
}
const result = await originRequest({
...params,
path: `/repos/${encodeURIComponent(ownerSlug)}/${encodeURIComponent(repoName)}/pulls/${encodeURIComponent(pullNumber)}`,
auth: 'installation',
})
if (!result.ok) {
throw new Error(
`Failed to get Origin pull request: ${originErrorMessage(result.body, result.status)}`,
)
}
return slimPull(asRecord(result.body))
}
/**
* Create an Origin pull request. Pass `dryRun: true` to preview, or
* `confirm: true` to create it.
*/
export async function createOriginPull(
params: OriginAccountInput &
OriginMutationInput & {
ownerSlug: string
repoName: string
title: string
head: string
base: string
body?: string
draft?: boolean
parentPullNumber?: string | number
},
) {
const ownerSlug = params.ownerSlug.trim()
const repoName = params.repoName.trim()
const title = params.title.trim()
const head = params.head.trim()
const base = params.base.trim()
if (!ownerSlug || !repoName || !title || !head || !base) {
throw new Error('ownerSlug, repoName, title, head, and base are required.')
}
const result = await originRequest({
...params,
path: `/repos/${encodeURIComponent(ownerSlug)}/${encodeURIComponent(repoName)}/pulls`,
method: 'POST',
auth: 'installation',
body: {
title,
head,
base,
...(params.body !== undefined ? { body: params.body } : {}),
...(params.draft !== undefined ? { draft: params.draft } : {}),
...(params.parentPullNumber !== undefined
? { parentPullNumber: String(params.parentPullNumber) }
: {}),
},
})
if (result.dryRun) {
return {
dryRun: true,
wouldCreate: true,
ownerSlug,
repoName,
title,
head,
base,
}
}
if (!result.ok) {
throw new Error(
`Failed to create Origin pull request: ${originErrorMessage(result.body, result.status)}`,
)
}
return slimPull(asRecord(result.body))
}
/**
* Update an Origin pull request. Pass `dryRun: true` to preview, or
* `confirm: true` to apply.
*/
export async function updateOriginPull(
params: OriginAccountInput &
OriginMutationInput & {
ownerSlug: string
repoName: string
pullNumber: string | number
title?: string
body?: string
state?: string
draft?: boolean
},
) {
const ownerSlug = params.ownerSlug.trim()
const repoName = params.repoName.trim()
const pullNumber = String(params.pullNumber).trim()
if (!ownerSlug || !repoName || !pullNumber) {
throw new Error('ownerSlug, repoName, and pullNumber are required.')
}
const body: Record<string, unknown> = {}
if (params.title !== undefined) body.title = params.title
if (params.body !== undefined) body.body = params.body
if (params.state !== undefined) body.state = params.state
if (params.draft !== undefined) body.draft = params.draft
const result = await originRequest({
...params,
path: `/repos/${encodeURIComponent(ownerSlug)}/${encodeURIComponent(repoName)}/pulls/${encodeURIComponent(pullNumber)}`,
method: 'PATCH',
auth: 'installation',
body,
})
if (result.dryRun) {
return {
dryRun: true,
wouldUpdate: true,
ownerSlug,
repoName,
pullNumber,
body,
}
}
if (!result.ok) {
throw new Error(
`Failed to update Origin pull request: ${originErrorMessage(result.body, result.status)}`,
)
}
return slimPull(asRecord(result.body))
}
/**
* Merge an Origin pull request. Pass `dryRun: true` to preview, or
* `confirm: true` to merge.
*/
export async function mergeOriginPull(
params: OriginAccountInput &
OriginMutationInput & {
ownerSlug: string
repoName: string
pullNumber: string | number
expectedHeadSha?: string
},
) {
const ownerSlug = params.ownerSlug.trim()
const repoName = params.repoName.trim()
const pullNumber = String(params.pullNumber).trim()
if (!ownerSlug || !repoName || !pullNumber) {
throw new Error('ownerSlug, repoName, and pullNumber are required.')
}
const result = await originRequest({
...params,
path: `/repos/${encodeURIComponent(ownerSlug)}/${encodeURIComponent(repoName)}/pulls/${encodeURIComponent(pullNumber)}/merge`,
method: 'POST',
auth: 'installation',
body: params.expectedHeadSha
? { expectedHeadSha: params.expectedHeadSha }
: {},
})
if (result.dryRun) {
return {
dryRun: true,
wouldMerge: true,
ownerSlug,
repoName,
pullNumber,
}
}
if (!result.ok) {
throw new Error(
`Failed to merge Origin pull request: ${originErrorMessage(result.body, result.status)}`,
)
}
return result.body
}
export default listOriginPulls
function slimPull(item: Record<string, unknown>): OriginPullSummary {
const head = isRecord(item.head) ? item.head : null
const base = isRecord(item.base) ? item.base : null
return {
number:
typeof item.number === 'string' || typeof item.number === 'number'
? item.number
: null,
title: stringField(item.title),
state: stringField(item.state),
draft: booleanField(item.draft),
merged: booleanField(item.merged),
head: stringField(head?.ref) ?? stringField(item.head),
base: stringField(base?.ref) ?? stringField(item.base),
url: stringField(item.url) ?? stringField(item.htmlUrl),
}
}