227 lines · 9.9 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 { env } from "cloudflare:workers";
import { z } from "zod";
import { loadIntegration, openCredentials, parseConfig, updateCredentials } from "./store";
import type { IntegrationCredentials } 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 HTTP_UNAUTHORIZED = 401;
const HTTP_TOO_MANY = 429;
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 (result.status === null) return { data: null, result, graphqlError: null };
  let parsed: { data?: T; errors?: Array<{ message: string; extensions?: { code?: string } }> };
  try {