165 lines · 7.2 KB
import { all, run } from "../db";
import { DEFAULT_LINEAR_MAPPING } from "~/lib/integrations";
import { extractIssueKeys } from "./linear-keys";
import { isSuccess, outboundFetch } from "./outbound";
import type { OutboundResult } from "./outbound";
import { openCredentials, parseConfig } from "./store";
import type { IntegrationRow } from "./store";
import type { DeliverInput } from "./deliver";

const LINEAR_GRAPHQL = "https://api.linear.app/graphql";
const MAX_KEYS_PER_EVENT = 5;
const STATE_RANK: Record<string, number> = {
  triage: 0,
  backlog: 1,
  unstarted: 2,
  started: 3,
  completed: 4,
  canceled: 4,
};

type LinearState = { id: string; name: string; type: string };
type LinearIssue = { id: string; identifier: string; state: LinearState; team: { states: { nodes: LinearState[] } } };

export async function linearGraphql<T>(
  token: string,
  query: string,
  variables: Record<string, unknown>,
): Promise<{ data: T | null; result: OutboundResult; graphqlError: string | null }> {
  const result = await outboundFetch(LINEAR_GRAPHQL, {
    headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
    body: JSON.stringify({ query, variables }),
  });
  if (!isSuccess(result)) return { data: null, result, graphqlError: null };
  let parsed: { data?: T; errors?: Array<{ message: string }> };
  try {
    parsed = JSON.parse(result.body);
  } catch {
    return { data: null, result: { ...result, error: "Linear returned invalid JSON" }, graphqlError: null };
  }
  if (parsed.errors?.length) {