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)))