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 UpdateIssueInput = LinearAuthInput & { /** Issue UUID or identifier (`ENG-123`). */ id: string title?: string description?: string assigneeId?: string stateId?: string projectId?: string teamId?: string priority?: number labelIds?: Array dueDate?: string parentId?: string estimate?: number confirm?: boolean dryRun?: boolean } function issueUpdatePayload(input: UpdateIssueInput): JsonRecord { const payload = compactRecord({ title: optionalString(input.title, 'title'), description: optionalString(input.description, 'description'), assigneeId: optionalString(input.assigneeId, 'assigneeId'), stateId: optionalString(input.stateId, 'stateId'), projectId: optionalString(input.projectId, 'projectId'), teamId: optionalString(input.teamId, 'teamId'), priority: optionalPriority(input.priority), labelIds: optionalStringArray(input.labelIds, 'labelIds'), dueDate: optionalString(input.dueDate, 'dueDate'), parentId: optionalString(input.parentId, 'parentId'), estimate: optionalNumber(input.estimate, 'estimate'), }) if (Object.keys(payload).length === 0) { throw new Error('update-issue requires at least one field to change.') } return payload } /** * Update a Linear issue. Requires `confirm: true`, or use `dryRun: true`. * Needs `write`. * @example * import updateIssue from 'kody:@kody/linear/update-issue' * const preview = await updateIssue({ * id: 'ENG-123', * stateId: 'state-uuid', * dryRun: true, * }) */ export async function updateIssue( input: UpdateIssueInput, ): Promise> { const id = requireString(input.id, 'id') const payload = issueUpdatePayload(input) if (input.dryRun) { return { dryRun: true, wouldCall: { mutation: 'issueUpdate', id, input: payload } } } if (input.confirm !== true) { throw new Error( 'Updating a Linear issue requires confirm: true after explicit user approval, or dryRun: true.', ) } const result = await linearGraphql<{ issueUpdate: { success?: boolean; issue?: unknown } }>({ ...input, operation: 'issueUpdate', query: `mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { ${ISSUE_FIELDS} } } }`, variables: { id, input: payload }, }) if (!result.data.issueUpdate?.success || !result.data.issueUpdate.issue) { throw new Error('Linear issueUpdate did not return an issue.') } return mapIssue(result.data.issueUpdate.issue) } /** * Update a Linear issue. Requires `confirm: true`, or use `dryRun: true`. * @example * import updateIssue from 'kody:@kody/linear/update-issue' * const preview = await updateIssue({ id: 'ENG-123', title: 'Renamed', dryRun: true }) */ export default async function updateIssueEntrypoint( params: Partial & Record = {}, ) { const input = requireRecord(params, 'update-issue') return updateIssue({ id: requireString(input.id, 'id'), title: optionalString(input.title, 'title'), description: optionalString(input.description, 'description'), assigneeId: optionalString(input.assigneeId, 'assigneeId'), stateId: optionalString(input.stateId, 'stateId'), projectId: optionalString(input.projectId, 'projectId'), teamId: optionalString(input.teamId, 'teamId'), 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'), }) }