diff --git a/update_notion/index.js b/update_notion/index.js index 2a79cb1..7feba7f 100644 --- a/update_notion/index.js +++ b/update_notion/index.js @@ -15,7 +15,7 @@ try { 'refs/heads/dev', ] - if (allowedBranches.indexOf(GITHUB_REF) !== -1) { + if (allowedBranches.indexOf(GITHUB_REF) !== -1) { const branchName = GITHUB_REF.split('/').pop() _updateNotionStatuses(branchName) } @@ -24,15 +24,94 @@ try { } function extractPrNumber (commitMessage) { - const match = commitMessage - .match(/#[0-9]{1,4}/) + // get pr number from commit message + const match = commitMessage.match(/#(\d+)/) if (match && match.length) { - return match[0].substring(1) + return Number(match[0].substring(1)) } return undefined } +const repoNameToNotionDatabaseProperty = { + 'coursedogv3': 'CoursedogV3 PR', + 'coursedog-catalog': 'Coursedog Catalog PR', +} + +const repoNameToPRStatusLink = { + 'coursedogv3': { + Open: '951fe09381834d8eb24a500537b5a5ca', + Merged: '85e90beb439c4897badd4e5effb73a89', + }, + 'coursedog-catalog': { + Open: '6d90bd1de3b64b09ac082f87e899c5bc', + Merged: '49e183df17dd4995bac9edac14e1ac5c', + }, +} + +async function updatePRStatus (prNumber, status) { + const NOTION_DATABASE_TOKEN = core.getInput('NOTION_DATABASE_TOKEN') + const NOTION_API_KEY = core.getInput('NOTION_API_KEY') + const NOTION_PR_DATABASE_TOKEN = core.getInput('NOTION_PR_DATABASE_TOKEN') || 'd1a47b75a15c438ebae10cd57c8f1101' + const { GITHUB_REPOSITORY } = process.env + const [ repositoryName ] = GITHUB_REPOSITORY.split('/') + + const masterTaskDatabase = new Notion({ + apiKey: NOTION_API_KEY, + databaseToken: NOTION_DATABASE_TOKEN, + }) + + const linkToPR = `${repositoryName}/pull/${prNumber}` + // Check if there is a task with the PR number + // If there is, update the status of the task + const taskWithPRNumber = await masterTaskDatabase.getTaskByGithubPRInput(linkToPR) + if (taskWithPRNumber && taskWithPRNumber.results.length) { + await masterTaskDatabase.updateByPR(linkToPR, status) + return + } + + const coursedogV3PRDatabaseNotionUtil = new Notion({ + apiKey: NOTION_API_KEY, + databaseToken: NOTION_PR_DATABASE_TOKEN, + }) + // If there isn't take the following steps: + // 1. Search with the PR number in the PR database + // 2. Get the relevant task IDs from the PR database + // 3. Get the details of the tasks from the master task database + // 4. Get the list of PRs from the task + // 5. Check the status of the PRs + // 6. If all the PR statuses are merged, update the status of the task + const taskWithPRNumberFromMasterTask = await coursedogV3PRDatabaseNotionUtil.getPageByPRNumber(prNumber) + const taskIds = taskWithPRNumberFromMasterTask.properties['Task']?.['relation'].map( + (item) => item.id + ) + const listOfTasks = await Promise.all(taskIds.map((eachTaskId) => masterTaskDatabase.getPageById(eachTaskId))) + for (let index = 0; index < listOfTasks.length; index++) { + const eachTask = listOfTasks[index] + const listOfLinkedPRPageIds = eachTask.properties[repoNameToNotionDatabaseProperty[repositoryName]]?.['relation'].map( + (item) => item.id + ) + + const listOfLinkedPRPages = await Promise + .all(listOfLinkedPRPageIds + .map((eachPRPageId) => coursedogV3PRDatabaseNotionUtil.getPageById(eachPRPageId)) + ) + + const PRStatuses = listOfLinkedPRPages + .map((eachPRPage) => eachPRPage + .properties['State'] + .relation + .map((item) => item.id) + ) + .flat() + .map((eachPRStatusId) => eachPRStatusId.replace(/-/g, '')) + + if (PRStatuses.every((status)=> status === repoNameToPRStatusLink[repositoryName]['Open'])) { + await masterTaskDatabase.updateTaskStatusById(eachTask.id, status) + } + } +} + /** * Function to update the status of the tasks based on * the recent commit is getting merged to which branch @@ -59,7 +138,7 @@ async function _updateNotionStatuses (branch) { }) const [ githubOwner, repositoryName ] = GITHUB_REPOSITORY.split('/') - + // Get most recent commit to branch const { data } = await octokit.rest.repos.getCommit({ owner: githubOwner, @@ -80,23 +159,23 @@ async function _updateNotionStatuses (branch) { case 'main': if (commitMessage.includes('from coursedog/staging')) { // Update all tasks that are in Completed Staging to Completed Prod - notionUtil.updateByStatus('Completed (Staging)', 'Completed (Production)') + await notionUtil.updateByStatus('Completed (Staging)', 'Completed (Production)') } else if (commitMessage.match(/#+[0-9]/)) { // direct from open PR to staging const prNumber = extractPrNumber(commitMessage) if (!prNumber) return - notionUtil.updateByPR(`${repositoryName}/pull/${prNumber}`, 'Completed (Production)') + await updatePRStatus(prNumber, 'Completed (Production)') } break case 'staging': if (commitMessage.includes('from coursedog/dev')) { // Update all tasks that are in Completed Dev to Completed Staging - notionUtil.updateByStatus('Completed (Dev)', 'Completed (Staging)') + await notionUtil.updateByStatus('Completed (Dev)', 'Completed (Staging)') } else if (commitMessage.match(/#+[0-9]/)) { // direct from open PR to staging const prNumber = extractPrNumber(commitMessage) if (!prNumber) return - notionUtil.updateByPR(`${repositoryName}/pull/${prNumber}`, 'Completed (Staging)') + await updatePRStatus(prNumber, 'Completed (Staging)') } break case 'dev': @@ -104,10 +183,10 @@ async function _updateNotionStatuses (branch) { // direct from open PR to dev const prNumber = extractPrNumber(commitMessage) if (!prNumber) return - notionUtil.updateByPR(`${repositoryName}/pull/${prNumber}`, 'Completed (Dev)') + await updatePRStatus(prNumber, 'Completed (Dev)') } break - + default: break } diff --git a/utils/notion.js b/utils/notion.js index 8405247..2684d8f 100644 --- a/utils/notion.js +++ b/utils/notion.js @@ -1,18 +1,19 @@ const fs = require('fs') -const { Client }= require('@notionhq/client') +const { Client } = require('@notionhq/client') class Notion { constructor ({ apiKey, databaseToken, testDatabase }) { // Retrieving all the input parameters relevant to notion this.notionDatabaseToken = databaseToken this.testDatabase = testDatabase - + // Initializing the notion client this.client = new Client({ auth: apiKey }) this.labels = { GITHUB_PR: 'GitHub PR', STATUS: 'Status', DATE_COMPLETED: 'Date Completed', + PR_NUMBER: 'PR Number', } } @@ -64,6 +65,69 @@ class Notion { } } + async getPageById (pageId) { + return this.client.pages.retrieve({ + page_id: pageId, + }) + } + + async getPageByPRNumber (prNumber) { + const databaseId = this.notionDatabaseToken + const listOfTasks = await this.client.databases.query({ + database_id: databaseId, + filter: { + and: [ + { + property: this.labels.PR_NUMBER, + number: { + equals: prNumber, + }, + }, + ], + }, + }) + return listOfTasks.results?.[0] + } + + async getTaskByGithubPRInput (prLink) { + const databaseId = this.notionDatabaseToken + + const listOfTasks = await this.client.databases.query({ + database_id: databaseId, + filter: { + and: [ + { + property: this.labels.GITHUB_PR, + text: { + contains: prLink, + }, + }, + ], + }, + }) + + return listOfTasks + } + + async updateTaskStatusById (taskId, newStatus) { + const updatedTasks = await this.client.pages.update({ + page_id: taskId, + properties: { + [this.labels.STATUS]: { + select: { + name: newStatus, + }, + }, + [this.labels.DATE_COMPLETED]: { + date: { + start: new Date().toISOString(), + }, + }, + }, + }) + console.log('Updated', updatedTasks) + } + /** * Function to promote the task by the Github PR field * @@ -173,7 +237,7 @@ class Notion { ], }) } - + /** * Function to update an existing feature block * @@ -217,7 +281,7 @@ class Notion { }, }, }) - + const pageBlocks = await this.client.blocks.children.list({ block_id: card.id, page_size: 100, @@ -226,9 +290,9 @@ class Notion { const dividerIndex = pageBlocks.results .reverse() .findIndex(block => block.type === 'unsupported') - + const blocksToDelete = pageBlocks.results.slice(0, dividerIndex) - + // NOTE: Delete all blocks after the last unsupported block (likely the divider) if (blocksToDelete.length) { for (const block of blocksToDelete) { @@ -237,7 +301,7 @@ class Notion { }) } } - + // NOTE: Restore the feature block await this.client.blocks.children.append({ block_id: card.id, @@ -261,7 +325,7 @@ class Notion { * @returns {String} Name of the product which is to be used to tag the file */ __getProduct (filePath) { - + const { GITHUB_REPOSITORY, } = process.env @@ -358,7 +422,7 @@ class Notion { }) } } - + } module.exports = Notion