From 9c59eb379831703f4dc2c7e4177e67e670dba017 Mon Sep 17 00:00:00 2001 From: dphuang2 Date: Thu, 23 Nov 2023 14:26:17 -0800 Subject: [PATCH] [ENG-1438] Throw helpful error message when konfig.yaml is not configured correctly when revalidating portal (#390) * FileNotFoundError * docs(changeset): improve error messaging when file not found from github --- .../.changeset/nice-dancers-burn.md | 5 + .../src/commands/revalidate-portal.ts | 40 +++--- .../src/pages/api/revalidate-portal.ts | 120 ++++++++++-------- .../src/utils/github-get-file-content.ts | 41 +++++- 4 files changed, 128 insertions(+), 78 deletions(-) create mode 100644 generator/konfig-dash/.changeset/nice-dancers-burn.md diff --git a/generator/konfig-dash/.changeset/nice-dancers-burn.md b/generator/konfig-dash/.changeset/nice-dancers-burn.md new file mode 100644 index 0000000000..642fe28f4c --- /dev/null +++ b/generator/konfig-dash/.changeset/nice-dancers-burn.md @@ -0,0 +1,5 @@ +--- +'konfig-cli': minor +--- + +improve error messaging when file not found from github diff --git a/generator/konfig-dash/packages/konfig-cli/src/commands/revalidate-portal.ts b/generator/konfig-dash/packages/konfig-cli/src/commands/revalidate-portal.ts index 143921a619..51c60e5add 100644 --- a/generator/konfig-dash/packages/konfig-cli/src/commands/revalidate-portal.ts +++ b/generator/konfig-dash/packages/konfig-cli/src/commands/revalidate-portal.ts @@ -1,5 +1,5 @@ import { CliUx, Command, Flags } from '@oclif/core' -import axios from 'axios' +import axios, { AxiosError } from 'axios' function getRevalidatePortalUrl({ dev }: { dev: boolean }) { if (dev) return 'http://127.0.0.1:3000/api/revalidate-portal' @@ -41,21 +41,29 @@ export default class RevalidatePortal extends Command { CliUx.ux.action.start( `Revalidating portal for "${flags.owner}/${flags.repository}"` ) - const result = await axios.post(url, { - owner: flags.owner, - repo: flags.repository, - }) - if (result.data['revalidated']) { - CliUx.ux.action.stop() - this.log( - `✅ Successfully revalidated portal at ${flags.owner}/${flags.repository}` - ) - this.debug('Paths Revalidated:') - this.debug(result.data['revalidated'].join('\n')) - } else { - this.error( - `Failed to revalidate ${flags.organizationId}/${flags.portalId}: "${result.data}"` - ) + try { + const result = await axios.post(url, { + owner: flags.owner, + repo: flags.repository, + }) + if (result.data['revalidated']) { + CliUx.ux.action.stop() + this.log( + `✅ Successfully revalidated portal at ${flags.owner}/${flags.repository}` + ) + this.debug('Paths Revalidated:') + this.debug(result.data['revalidated'].join('\n')) + } else { + this.error( + `Failed to revalidate ${flags.organizationId}/${flags.portalId}: "${result.data}"` + ) + } + } catch (e) { + if (e instanceof AxiosError) { + if (typeof e.response?.data === 'string') + CliUx.ux.error(e.response?.data) + } + throw e } } } diff --git a/generator/konfig-next-app/src/pages/api/revalidate-portal.ts b/generator/konfig-next-app/src/pages/api/revalidate-portal.ts index b32eb4cd5d..a8e1b7f29e 100644 --- a/generator/konfig-next-app/src/pages/api/revalidate-portal.ts +++ b/generator/konfig-next-app/src/pages/api/revalidate-portal.ts @@ -7,6 +7,7 @@ import { githubGetKonfigYamls } from '@/utils/github-get-konfig-yamls' import { createOctokitInstance } from '@/utils/octokit' import { collectAllDocuments } from '@/utils/collect-all-documents' import { findDomainsForOwnerAndRepo } from '@/utils/find-domains-for-owner-and-repo' +import { FileNotFoundError } from '@/utils/github-get-file-content' const requestBodySchema = z.object({ owner: z.string(), @@ -35,78 +36,85 @@ export default async function handler( ]), ] - // revalidate reference page - const { navbarData } = await githubGetReferenceResources({ owner, repo }) - navbarData.forEach(({ links }) => { - links.forEach(({ link }) => { - toRevalidate.push(link) + try { + // revalidate reference page + const { navbarData } = await githubGetReferenceResources({ owner, repo }) + navbarData.forEach(({ links }) => { + links.forEach(({ link }) => { + toRevalidate.push(link) + }) }) - }) - const { navbarData: navbarDataWithoutOwnerAndRepo } = - await githubGetReferenceResources({ owner, repo, omitOwnerAndRepo: true }) - navbarDataWithoutOwnerAndRepo.forEach(({ links }) => { - links.forEach(({ link }) => { - for (const domain of domains) { - toRevalidate.push(`/${domain}${link}`) - } + const { navbarData: navbarDataWithoutOwnerAndRepo } = + await githubGetReferenceResources({ owner, repo, omitOwnerAndRepo: true }) + navbarDataWithoutOwnerAndRepo.forEach(({ links }) => { + links.forEach(({ link }) => { + for (const domain of domains) { + toRevalidate.push(`/${domain}${link}`) + } + }) }) - }) - const demos = await generateDemosDataFromGithub({ - orgId: owner, - portalId: repo, - }) + const demos = await generateDemosDataFromGithub({ + orgId: owner, + portalId: repo, + }) - if (demos.result !== 'error') { - for (const demo of demos.portal.demos) { - const oldDemoPath = `/${owner}/${repo}/${demo.id}` - const newDemoPath = `/${owner}/${repo}/demo/${demo.id}` - toRevalidate.push(oldDemoPath) - toRevalidate.push(newDemoPath) + if (demos.result !== 'error') { + for (const demo of demos.portal.demos) { + const oldDemoPath = `/${owner}/${repo}/${demo.id}` + const newDemoPath = `/${owner}/${repo}/demo/${demo.id}` + toRevalidate.push(oldDemoPath) + toRevalidate.push(newDemoPath) - for (const domain of domains) { - const newDomainPath = `/${domain}/demo/${demo.id}` - toRevalidate.push(newDomainPath) + for (const domain of domains) { + const newDomainPath = `/${domain}/demo/${demo.id}` + toRevalidate.push(newDomainPath) + } } } - } - const octokit = await createOctokitInstance({ owner, repo }) - const konfigYamls = await githubGetKonfigYamls({ owner, repo, octokit }) - if (konfigYamls !== null) { - for (const konfigYaml of konfigYamls) { - if (konfigYaml.content.portal?.documentation !== undefined) { - const links = collectAllDocuments({ - docConfig: konfigYaml.content.portal.documentation, - }) - for (const link of links) { - toRevalidate.push(`/${owner}/${repo}/docs/${link.id}`) - for (const domain of domains) { - toRevalidate.push(`/${domain}/docs/${link.id}`) + const octokit = await createOctokitInstance({ owner, repo }) + const konfigYamls = await githubGetKonfigYamls({ owner, repo, octokit }) + if (konfigYamls !== null) { + for (const konfigYaml of konfigYamls) { + if (konfigYaml.content.portal?.documentation !== undefined) { + const links = collectAllDocuments({ + docConfig: konfigYaml.content.portal.documentation, + }) + for (const link of links) { + toRevalidate.push(`/${owner}/${repo}/docs/${link.id}`) + for (const domain of domains) { + toRevalidate.push(`/${domain}/docs/${link.id}`) + } } } } } - } - const revalidated: string[] = [] + const revalidated: string[] = [] - for (const path of toRevalidate) { - try { - await res.revalidate(path, { unstable_onlyGenerated: true }) - revalidated.push(path) - } catch (e) { - if (e instanceof Error) { - if (e.message.includes('404')) { - console.log(`Tried to revalidate ${path} but got 404`) - continue + for (const path of toRevalidate) { + try { + await res.revalidate(path, { unstable_onlyGenerated: true }) + revalidated.push(path) + } catch (e) { + if (e instanceof Error) { + if (e.message.includes('404')) { + console.log(`Tried to revalidate ${path} but got 404`) + continue + } } + throw e } - throw e } - } - return res.json({ - revalidated, - }) + return res.json({ + revalidated, + }) + } catch (e) { + if (e instanceof FileNotFoundError) { + return res.status(404).send(e.message) + } + throw e + } } diff --git a/generator/konfig-next-app/src/utils/github-get-file-content.ts b/generator/konfig-next-app/src/utils/github-get-file-content.ts index d808466d09..c530f5e04e 100644 --- a/generator/konfig-next-app/src/utils/github-get-file-content.ts +++ b/generator/konfig-next-app/src/utils/github-get-file-content.ts @@ -16,6 +16,32 @@ function computeCacheKey({ return `${owner}/${repo}/${path}` } +export class FileNotFoundError extends Error { + innerError: Error + name = 'FileNotFoundError' + + constructor(innerError: Error) { + let message = + "Make sure you've configured 'konfig.yaml' to point to the correct files." + if ( + 'response' in innerError && + typeof innerError?.response === 'object' && + innerError?.response !== null && + 'url' in innerError?.response && + typeof innerError.response?.url === 'string' + ) { + // parse "https://api.github.com/repos/passiv/snaptrade-sdks/contents/docs%2Frecommended-functionality.md" for "docs/recommended-functionality.md" + const path = innerError.response.url + .split('repos/')[1] + .split('/contents/')[1] + const decoded = decodeURIComponent(path) + message += ` Could not find file: "${decoded}"` + } + super(message) + this.innerError = innerError + } +} + export async function githubGetFileContent({ octokit, owner, @@ -62,11 +88,14 @@ export async function githubGetFileContent({ } else { throw new Error('The specified path does not point to a file') } - } catch (error) { - if (error instanceof Error) - console.error( - `Error occurred while getting file content: ${error.message}` - ) - throw error + } catch (e) { + if (e instanceof Error) { + if ('status' in e && e.status === 404) { + throw new FileNotFoundError(e) + } else { + console.error(`Error occurred while getting file content: ${e.message}`) + } + } + throw e } }