From 4eee9bc38ab6ba8d7fd24c781c399d77dd8dbc3f Mon Sep 17 00:00:00 2001 From: Brian Beckerle <49686530+brainbicycle@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:19:21 +0200 Subject: [PATCH] chore: util script for exporting notion cards to jira (#10983) * skeleton script for fetching from notion * create tickets in jira * fix linking * add explanatory comment * fix usage error * bump --- package.json | 1 + scripts/utils/exportNotionTicketsToJira.ts | 145 +++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 scripts/utils/exportNotionTicketsToJira.ts diff --git a/package.json b/package.json index 7b5c58f80c1..00ee1aca08e 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "detect-secrets:hook": "scripts/detect-secrets/detect-secrets.sh hook", "detect-secrets:rebuild": "scripts/detect-secrets/detect-secrets.sh rebuild", "doctor": "./scripts/utils/doctor.js", + "export-notion-to-jira": "tsx scripts/utils/exportNotionTicketsToJira.ts", "flip_table": "./scripts/utils/flip-table", "flip_table_extreme": "./scripts/utils/flip-table-extreme", "generate-cities-cache": "node scripts/city-guide/generate-cities-cache.js", diff --git a/scripts/utils/exportNotionTicketsToJira.ts b/scripts/utils/exportNotionTicketsToJira.ts new file mode 100644 index 00000000000..a74b5cf7076 --- /dev/null +++ b/scripts/utils/exportNotionTicketsToJira.ts @@ -0,0 +1,145 @@ +#!/usr/bin/env node +/// +// @ts-check +"use strict" + +import { resolve } from "path" +import chalk from "chalk" +import { config } from "dotenv" +import { hideBin } from "yargs/helpers" +import yargs from "yargs/yargs" + +config({ path: resolve(__dirname, "../../.env.releases") }) + +/** + * + * Convenience script for fetching notion cards from our mobile QA and creating Jira issues for them. + * Usage: yarn export-notion-to-jira + * To get the database id, go to the latest Mobile QA page, scroll to the bugs section, + * click on the dots next to "Board View" and select "Copy Link". + * The link should be of format: https://www.notion.so/artsy/?v=&pvs= + * + */ + +const NOTION_API_TOKEN = process.env.NOTION_API_TOKEN +const JIRA_BASE_URL = process.env.JIRA_BASE_URL +const JIRA_EMAIL = process.env.JIRA_EMAIL +const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN + +if (!NOTION_API_TOKEN) { + console.error(chalk.bold.red("Missing NOTION_API_TOKEN in environment variables.")) + process.exit(1) +} + +if (!JIRA_BASE_URL || !JIRA_EMAIL || !JIRA_API_TOKEN) { + console.error(chalk.bold.red("Missing Jira credentials in environment variables.")) + process.exit(1) +} + +const argv = yargs(hideBin(process.argv)).argv as any +const databaseId = argv._[0] + +if (!databaseId) { + console.error(chalk.bold.red("Usage: yarn export-notion-to-jira ")) + process.exit(1) +} + +async function fetchNotionDatabase(databaseId: string) { + try { + const response = await fetch(`https://api.notion.com/v1/databases/${databaseId}/query`, { + method: "POST", + headers: { + Authorization: `Bearer ${NOTION_API_TOKEN}`, + "Content-Type": "application/json", + "Notion-Version": "2022-06-28", + }, + }) + + if (!response.ok) { + throw new Error(`Failed to fetch database: ${response.status} ${response.statusText}`) + } + + const data = await response.json() + console.log(chalk.bold.green("Successfully fetched Notion database")) + return data + } catch (error) { + console.error(chalk.bold.red("Error fetching Notion database:")) + console.error(error) + } +} + +async function createJiraIssue(issueSummary: string, issueLink: string) { + try { + const auth = Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64") + const response = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue`, { + method: "POST", + headers: { + Authorization: `Basic ${auth}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + fields: { + project: { + key: "APPL", + }, + summary: issueSummary, + description: { + type: "doc", + version: 1, + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Auto-generated from mobile QA Notion card ", + }, + { + type: "text", + text: "here", + marks: [ + { + type: "link", + attrs: { + href: issueLink, + }, + }, + ], + }, + ], + }, + ], + }, + issuetype: { + name: "Bug", + }, + }, + }), + }) + + if (!response.ok) { + console.log(await response.text()) + throw new Error(`Failed to create Jira issue: ${response.status} ${response.statusText}`) + } + + const data = await response.json() + console.log(chalk.bold.green("Successfully created Jira issue:")) + console.log(JSON.stringify(data, null, 2)) + } catch (error) { + console.error(chalk.bold.red("Error creating Jira issue:")) + console.error(error) + } +} + +async function main() { + const notionData = await fetchNotionDatabase(databaseId) + if (notionData && notionData.results) { + for (const page of notionData.results) { + const issueSummary = page.properties.Name.title[0]?.plain_text || "No Title" + const notionPageUrl = page.url + await createJiraIssue(issueSummary, notionPageUrl) + } + } +} + +main().catch((err) => console.error(chalk.bold.red(err)))