From e0a02721e01ce172683d4d1ec446f9e152967699 Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 19 Nov 2024 12:24:19 -0500 Subject: [PATCH 1/3] Optimize file fetching code --- src/content/github/common/fetchers.ts | 24 +--- src/content/github/file/main.tsx | 157 +++++++++++---------- src/content/github/file/utils/dropdown.tsx | 33 ++++- src/types.ts | 9 +- 4 files changed, 113 insertions(+), 110 deletions(-) diff --git a/src/content/github/common/fetchers.ts b/src/content/github/common/fetchers.ts index a61ace9..60b8e97 100644 --- a/src/content/github/common/fetchers.ts +++ b/src/content/github/common/fetchers.ts @@ -5,25 +5,6 @@ import { MessageType, } from "src/types"; -export async function getMetadata(url: string): Promise { - const response = await fetch(url, { - headers: { - "Accept": "application/json", - }, - }).then((response) => response.json()); - let branch = undefined; - if (response.payload.refInfo.refType === "branch") { - branch = response.payload.refInfo.name; - } - return { - owner: response.payload.repo.ownerLogin, - repo: response.payload.repo.name, - path: response.payload.path, - commit: response.payload.refInfo.currentOid, - branch: branch, - }; -} - export async function getFlags(metadata: FileMetadata): Promise { const payload = { service: "github", @@ -65,6 +46,11 @@ export async function getCommitReport( flag: string | undefined, component_id: string | undefined ): Promise { + // metadata.commit must be defined, check it before calling + if (!metadata.commit) { + throw new Error("getCommitReport called without commit sha"); + } + const payload = { service: "github", owner: metadata.owner, diff --git a/src/content/github/file/main.tsx b/src/content/github/file/main.tsx index 3857b6d..4fc4720 100644 --- a/src/content/github/file/main.tsx +++ b/src/content/github/file/main.tsx @@ -1,4 +1,3 @@ -import React from "dom-chef"; import browser from "webextension-polyfill"; import alpha from "color-alpha"; import Drop from "tether-drop"; @@ -27,7 +26,6 @@ import { import { colors } from "../common/constants"; import { createDropdown } from "./utils/dropdown"; import { - getMetadata, getComponents, getCommitReport, getFlags, @@ -35,7 +33,7 @@ import { } from "../common/fetchers"; import { print } from "src/utils"; import { isFileUrl } from "../common/utils"; -import Sentry from '../../common/sentry'; +import Sentry from "../../common/sentry"; const globals: { coverageReport?: FileCoverageReport; @@ -47,7 +45,7 @@ const globals: { prompt?: HTMLElement; } = {}; -init() +init(); function init(): Promise { // this event discovered by "reverse-engineering GitHub" @@ -62,22 +60,61 @@ function init(): Promise { } async function main(): Promise { - try { - if (!isFileUrl(document.URL)) { - print("file not detected at current URL"); - return; - } + const urlMetadata = getMetadataFromURL(); + if (!urlMetadata) { + print("file not detected at current URL"); + return; + } - let metadata: FileMetadata; - metadata = await getMetadata(document.URL); + globals.coverageButton = createCoverageButton(); - globals.coverageButton = createCoverageButton(); + process(urlMetadata).catch((e) => { + print("unexpected error", e); + updateButton("Coverage: ⚠"); + }); +} - process(metadata) - } catch (e) { - Sentry.captureException(e) - throw e +function getMetadataFromURL(): { [key: string]: string } | null { + const regexp = + /\/(?.+?)\/(?.+?)\/blob\/(?.+?)\/(?.+?)$/; + const matches = regexp.exec(window.location.pathname); + const groups = matches?.groups; + if (!groups) { + return null; + } + + const branch = groups.branch; + const commitMatch = branch.match(/[\da-f]+/); + + // branch could be a commit sha + if ( + commitMatch && + commitMatch[0].length == branch.length && + (groups.branch.length === 40 || branch.length === 7) + ) { + // branch is actually a commit sha + let commit = branch; + + // if it's a short sha, we need to get the full sha + if (commit.length === 7) { + const commitLink = document.querySelector( + `[href^="/${groups.owner}/${groups.repo}/tree/${commit}"]` + ); + if (!commitLink) + throw new Error("Could not find commit link from short sha"); + const longSha = commitLink + .getAttribute("href") + ?.match(/[\da-f]{40}/)?.[0]; + if (!longSha) throw new Error("Could not get long sha from commit link"); + commit = longSha; + } + + return { + ...groups, + commit, + }; } + return groups; } async function process(metadata: FileMetadata): Promise { @@ -111,17 +148,16 @@ async function process(metadata: FileMetadata): Promise { previousElement: globals.coverageButton!, selectedOptions: selectedFlags, onClick: handleFlagClick, - }) - .then(({ button, list }) => { - globals.flagsButton = button; - globals.flagsDrop = new Drop({ - target: button, - content: list, - classes: "drop-theme-arrows codecov-z1 codecov-bg-white", - position: "bottom right", - openOn: "click", - }); - }) + }).then(({ button, list }) => { + globals.flagsButton = button; + globals.flagsDrop = new Drop({ + target: button, + content: list, + classes: "drop-theme-arrows codecov-z1 codecov-bg-white", + position: "bottom right", + openOn: "click", + }); + }); } const components = await getComponents(metadata); @@ -134,7 +170,7 @@ async function process(metadata: FileMetadata): Promise { return []; }); - // TODO: allow setting selected flags for different files at the same time + // TODO: allow setting selected components for different files at the same time if ( selectedComponents.length > 0 && _.intersection(components, selectedComponents).length === 0 @@ -151,34 +187,36 @@ async function process(metadata: FileMetadata): Promise { previousElement: globals.coverageButton!, onClick: handleComponentClick, selectedOptions: selectedComponents, - }) - .then(({ button, list }) => { - globals.componentsButton = button; - globals.componentsDrop = new Drop({ - target: button, - content: list, - classes: "drop-theme-arrows codecov-z1 codecov-bg-white", - position: "bottom right", - openOn: "click", - }); - }) + }).then(({ button, list }) => { + globals.componentsButton = button; + globals.componentsDrop = new Drop({ + target: button, + content: list, + classes: "drop-theme-arrows codecov-z1 codecov-bg-white", + position: "bottom right", + openOn: "click", + }); + }); } + // If commit sha is defined use that, otherwise just branch name + const getReportFn = metadata.commit ? getCommitReport : getBranchReport; + let coverageReportResponses: Array; try { if (selectedFlags?.length > 0) { coverageReportResponses = await Promise.all( - selectedFlags.map((flag) => getCommitReport(metadata, flag, undefined)) + selectedFlags.map((flag) => getReportFn(metadata, flag, undefined)) ); } else if (selectedComponents?.length > 0) { coverageReportResponses = await Promise.all( selectedComponents.map((component) => - getCommitReport(metadata, undefined, component) + getReportFn(metadata, undefined, component) ) ); } else { coverageReportResponses = await Promise.all([ - await getCommitReport(metadata, undefined, undefined), + await getReportFn(metadata, undefined, undefined), ]); } } catch (e) { @@ -220,7 +258,6 @@ async function process(metadata: FileMetadata): Promise { if (_.isEmpty(coverageReport)) { updateButton(`Coverage: N/A`); globals.coverageReport = {}; - await promptPastReport(metadata); return; } @@ -232,40 +269,6 @@ async function process(metadata: FileMetadata): Promise { animateAndAnnotateLines(noVirtLineSelector, annotateLine); } -async function promptPastReport(metadata: FileMetadata): Promise { - if (!metadata.branch) { - return; - } - const response = await getBranchReport(metadata); - const regexp = /app.codecov.io\/github\/.*\/.*\/commit\/(?.*)\/blob/; - const matches = regexp.exec(response.commit_file_url); - const commit = matches?.groups?.commit; - if (!commit) { - throw new Error("Could not parse commit hash from response for past coverage report") - } - const link = document.URL.replace( - `blob/${metadata.branch}`, - `blob/${commit}` - ); - globals.prompt = createPrompt( - - Coverage report not available for branch HEAD ( - {metadata.commit.substr(0, 7)}), most recent coverage report for this - branch available at commit {commit.substr(0, 7)} - - ); -} - -function createPrompt(child: any) { - const ref = document.querySelector('[data-testid="latest-commit"]') - ?.parentElement?.parentElement; - if (!ref) { - throw new Error("Could not find reference element to render prompt") - } - const prompt =
{child}
; - return ref.insertAdjacentElement("afterend", prompt) as HTMLElement; -} - function createCoverageButton() { const rawButton = document.querySelector('[data-testid="raw-button"]'); if (!rawButton) { diff --git a/src/content/github/file/utils/dropdown.tsx b/src/content/github/file/utils/dropdown.tsx index eb67632..8d03a6e 100644 --- a/src/content/github/file/utils/dropdown.tsx +++ b/src/content/github/file/utils/dropdown.tsx @@ -17,15 +17,34 @@ export async function createDropdown({ previousElement: HTMLElement; selectedOptions: string[]; }) { - const editButton = document - .querySelector('[data-testid="more-edit-button"]')! - .closest("div")!; - const dropdownButton = editButton.cloneNode(true) as HTMLElement; - const textNode: HTMLElement = dropdownButton.querySelector('[data-component="IconButton"]')!; + // Build the button out of the Raw/copy/download button group + const rawButton = document + .querySelector('[data-testid="download-raw-button"]')! + .closest("div"); + if (!rawButton) throw new Error("Could not find raw button group"); + const dropdownButton = rawButton.cloneNode(true) as HTMLElement; + // Remove copy button + const copyButton = dropdownButton.querySelector( + '[data-testid="copy-raw-button"]' + ); + if (!copyButton) throw new Error("Could not find copy button"); + dropdownButton.removeChild(copyButton); + // Replace download button with dropdown button + const downloadButton = dropdownButton.querySelector( + '[data-testid="download-raw-button"]' + ); + if (!downloadButton || !downloadButton.firstChild) + throw new Error("Could not find download button or it is missing children"); + const triangleDownSvg = document.querySelector(".octicon-triangle-down"); + if (!triangleDownSvg) throw new Error("Could not find triangle down svg"); + downloadButton.replaceChild(triangleDownSvg, downloadButton.firstChild); + + const textNode = dropdownButton.querySelector('[data-testid="raw-button"]'); + if (!textNode || !textNode.parentElement) + throw new Error("Could not find textNode"); textNode.innerHTML = ""; textNode.ariaDisabled = "false"; - textNode.parentElement!.ariaLabel = tooltip; - textNode.style.padding = `0 ${title.length * 5}px`; + textNode.parentElement.ariaLabel = tooltip; textNode.appendChild({title}); previousElement.insertAdjacentElement("afterend", dropdownButton); diff --git a/src/types.ts b/src/types.ts index 769435e..c008e4f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,5 @@ -export type FileMetadata = { - owner: string; - repo: string; - path: string; - commit: string; - branch: string | undefined; -}; +export type FileMetadata = { [key: string]: string }; +export type PRMetadata = { [key: string]: string }; export enum CoverageStatus { COVERED, From 86f11a64b0ba8be21fe72a949d02d60cc218e3aa Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 19 Nov 2024 12:38:33 -0500 Subject: [PATCH 2/3] Undo removing sentry --- src/content/github/file/main.tsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/content/github/file/main.tsx b/src/content/github/file/main.tsx index 4fc4720..039bb75 100644 --- a/src/content/github/file/main.tsx +++ b/src/content/github/file/main.tsx @@ -32,8 +32,8 @@ import { getBranchReport, } from "../common/fetchers"; import { print } from "src/utils"; -import { isFileUrl } from "../common/utils"; import Sentry from "../../common/sentry"; +import { isFileUrl } from "../common/utils"; const globals: { coverageReport?: FileCoverageReport; @@ -60,21 +60,21 @@ function init(): Promise { } async function main(): Promise { - const urlMetadata = getMetadataFromURL(); - if (!urlMetadata) { - print("file not detected at current URL"); - return; + try { + const urlMetadata = getMetadataFromURL(); + if (!urlMetadata) { + print("file not detected at current URL"); + return; + } + globals.coverageButton = createCoverageButton(); + process(urlMetadata); + } catch (e) { + Sentry.captureException(e); + throw e; } - - globals.coverageButton = createCoverageButton(); - - process(urlMetadata).catch((e) => { - print("unexpected error", e); - updateButton("Coverage: ⚠"); - }); } -function getMetadataFromURL(): { [key: string]: string } | null { +function getMetadataFromURL(): FileMetadata | null { const regexp = /\/(?.+?)\/(?.+?)\/blob\/(?.+?)\/(?.+?)$/; const matches = regexp.exec(window.location.pathname); From e0e856ef6ea2348f0d5cccc93b049a279e33a88d Mon Sep 17 00:00:00 2001 From: Spencer Murray Date: Tue, 19 Nov 2024 13:23:34 -0500 Subject: [PATCH 3/3] Nick suggestions --- src/content/github/file/main.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/content/github/file/main.tsx b/src/content/github/file/main.tsx index 039bb75..9d98fa1 100644 --- a/src/content/github/file/main.tsx +++ b/src/content/github/file/main.tsx @@ -204,7 +204,15 @@ async function process(metadata: FileMetadata): Promise { let coverageReportResponses: Array; try { - if (selectedFlags?.length > 0) { + if (selectedFlags?.length > 0 && selectedComponents?.length > 0) { + coverageReportResponses = await Promise.all( + selectedFlags.flatMap((flag) => + selectedComponents.map((component) => + getReportFn(metadata, flag, component) + ) + ) + ); + } else if (selectedFlags?.length > 0) { coverageReportResponses = await Promise.all( selectedFlags.map((flag) => getReportFn(metadata, flag, undefined)) ); @@ -215,9 +223,9 @@ async function process(metadata: FileMetadata): Promise { ) ); } else { - coverageReportResponses = await Promise.all([ + coverageReportResponses = [ await getReportFn(metadata, undefined, undefined), - ]); + ]; } } catch (e) { updateButton(`Coverage: ⚠`);