import { linearGraphql } from './client.ts'
import { ISSUE_FIELDS, mapIssue } from './models.ts'
import type {
DryRunResult,
JsonRecord,
LinearAuthInput,
LinearIssueSummary,
} from './types.ts'
import {
compactRecord,
optionalBoolean,
optionalNumber,
optionalPriority,
optionalString,
optionalStringArray,
requireRecord,
requireString,
} from './types.ts'
import { requireAuthMode } from './auth.ts'
export type CreateIssueInput = LinearAuthInput & {
title: string
teamId: string
description?: string
assigneeId?: string
stateId?: string
projectId?: string
priority?: number
labelIds?: Array<string>
dueDate?: string
parentId?: string
estimate?: number
confirm?: boolean
dryRun?: boolean
}
function issueCreatePayload(input: CreateIssueInput): JsonRecord {
return compactRecord({
title: requireString(input.title, 'title'),
teamId: requireString(input.teamId, 'teamId'),
description: optionalString(input.description, 'description'),
assigneeId: optionalString(input.assigneeId, 'assigneeId'),
stateId: optionalString(input.stateId, 'stateId'),
projectId: optionalString(input.projectId, 'projectId'),
priority: optionalPriority(input.priority),
labelIds: optionalStringArray(input.labelIds, 'labelIds'),
dueDate: optionalString(input.dueDate, 'dueDate'),
parentId: optionalString(input.parentId, 'parentId'),
estimate: optionalNumber(input.estimate, 'estimate'),
})
}
/**
* Create a Linear issue. Requires `confirm: true`, or use `dryRun: true`.
* Needs `issues:create` or `write`.
* @example
* import createIssue from 'kody:@kody/linear/create-issue'
* const preview = await createIssue({
* title: 'Follow up on onboarding',
* teamId: 'team-uuid',
* dryRun: true,
* })
*/
export async function createIssue(
input: CreateIssueInput,
): Promise<LinearIssueSummary | DryRunResult<{ mutation: string; input: JsonRecord }>> {
const payload = issueCreatePayload(input)
if (input.dryRun) {
return { dryRun: true, wouldCall: { mutation: 'issueCreate', input: payload } }
}
if (input.confirm !== true) {
throw new Error(
'Creating a Linear issue requires confirm: true after explicit user approval, or dryRun: true.',
)
}
const result = await linearGraphql<{
issueCreate: { success?: boolean; issue?: unknown }
}>({
...input,
operation: 'issueCreate',
query: `mutation CreateIssue($input: IssueCreateInput!) {
issueCreate(input: $input) {
success
issue { ${ISSUE_FIELDS} }
}
}`,
variables: { input: payload },
})
if (!result.data.issueCreate?.success || !result.data.issueCreate.issue) {
throw new Error('Linear issueCreate did not return an issue.')
}
return mapIssue(result.data.issueCreate.issue)
}
/**
* Create a Linear issue. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import createIssue from 'kody:@kody/linear/create-issue'
* const preview = await createIssue({ title: 'Bug', teamId: 'uuid', dryRun: true })
*/
export default async function createIssueEntrypoint(
params: Partial<CreateIssueInput> & Record<string, unknown> = {},
) {
const input = requireRecord(params, 'create-issue')
return createIssue({
title: requireString(input.title, 'title'),
teamId: requireString(input.teamId, 'teamId'),
description: optionalString(input.description, 'description'),
assigneeId: optionalString(input.assigneeId, 'assigneeId'),
stateId: optionalString(input.stateId, 'stateId'),
projectId: optionalString(input.projectId, 'projectId'),
priority: optionalPriority(input.priority),
labelIds: optionalStringArray(input.labelIds, 'labelIds'),
dueDate: optionalString(input.dueDate, 'dueDate'),
parentId: optionalString(input.parentId, 'parentId'),
estimate: optionalNumber(input.estimate, 'estimate'),
integrationName: optionalString(input.integrationName, 'integrationName'),
integration: optionalString(input.integration, 'integration'),
account: optionalString(input.account, 'account'),
secretName: optionalString(input.secretName, 'secretName'),
auth: requireAuthMode(input.auth),
confirm: optionalBoolean(input.confirm, 'confirm'),
dryRun: optionalBoolean(input.dryRun, 'dryRun'),
})
}