From fb1869bc3cc19aee0ab920d7a82ead8e74f0126a Mon Sep 17 00:00:00 2001 From: Andrew Jiang Date: Wed, 10 Apr 2024 08:43:36 +1000 Subject: [PATCH] Revert "fix: revalidate all paths before and after docs registration" (#630) --- .../revalidation/definition/__package__.yml | 54 +++--------- .../fdr-utils/src/__test__/buildUrl.test.ts | 39 --------- packages/commons/fdr-utils/src/buildUrl.ts | 24 ++--- .../fdr-utils/src/getAllUrlsFromDocsConfig.ts | 11 +-- packages/ui/docs-bundle/package.json | 1 - packages/ui/docs-bundle/src/pages/404.tsx | 4 +- .../src/pages/api/fern-docs/revalidate-all.ts | 9 +- .../pages/api/fern-docs/revalidate-all/v2.ts | 39 ++++++++- .../pages/api/fern-docs/revalidate-bulk.ts | 87 ------------------- .../fern-docs/{list-slugs.ts => sitemap.ts} | 18 ++-- .../src/pages/api/fern-docs/sitemap.xml.ts | 40 ++++----- .../docs-bundle/src/utils/getDocsPageProps.ts | 5 +- .../docs-bundle/src/utils/revalidate-types.ts | 11 --- pnpm-lock.yaml | 11 +-- servers/fdr/package.json | 2 +- servers/fdr/src/__test__/ete/ete.test.ts | 2 +- .../docs/v2/getDocsWriteV2Service.ts | 14 +-- .../revalidator/RevalidatorService.ts | 43 +++++---- .../revalidator/provideRevalidationClient.ts | 13 --- tests/fern/fern/docs.yml | 1 - 20 files changed, 121 insertions(+), 307 deletions(-) delete mode 100644 packages/commons/fdr-utils/src/__test__/buildUrl.test.ts delete mode 100644 packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-bulk.ts rename packages/ui/docs-bundle/src/pages/api/fern-docs/{list-slugs.ts => sitemap.ts} (76%) delete mode 100644 packages/ui/docs-bundle/src/utils/revalidate-types.ts delete mode 100644 servers/fdr/src/services/revalidator/provideRevalidationClient.ts diff --git a/fern/apis/revalidation/definition/__package__.yml b/fern/apis/revalidation/definition/__package__.yml index e7fdfe6fd3..b3370941e5 100644 --- a/fern/apis/revalidation/definition/__package__.yml +++ b/fern/apis/revalidation/definition/__package__.yml @@ -4,24 +4,6 @@ service: auth: false base-path: /api/fern-docs endpoints: - bulkRevalidate: - path: /revalidate-bulk - docs: Revalidates a list of URLs - display-name: Revalidate a list of URLs - method: POST - request: - name: BulkRevalidateRequest - body: - properties: - host: - type: string - docs: | - The host you want to revalidate the docs for. - slugs: - type: list - docs: | - The paths to revalidate, sans initial slash. i.e. `path/to/page` - response: BulkRevalidateResponse revalidateAllV2: path: /revalidate-all/v2 docs: Revalidates a docs website @@ -29,41 +11,31 @@ service: method: POST request: name: CreateRevalidateAllV2Request - headers: - x-fern-host: + headers: + x-fern-host: type: string docs: | The host you want to revalidate the docs for. + query-parameters: + basePath: + type: string + docs: | + The base path of the docs you want to revalidate. body: properties: host: type: string docs: | The host you want to revalidate the docs for. - response: BulkRevalidateResponse - listSlugs: - path: /list-slugs - display-name: Get all slugs for docs site - method: GET - request: - name: GetSitemapRequest - headers: - x-fern-host: optional - response: list + response: RevalidateAllV2Response types: - BulkRevalidateResponse: + RevalidateAllV2Response: properties: - successfulRevalidations: list - failedRevalidations: list - - SuccessfulRevalidation: - properties: - success: literal - url: string + successfulRevalidations: list + failedRevalidations: list - FailedRevalidation: + SuccessfulRevalidations: properties: - success: literal + success: string url: string - message: optional diff --git a/packages/commons/fdr-utils/src/__test__/buildUrl.test.ts b/packages/commons/fdr-utils/src/__test__/buildUrl.test.ts deleted file mode 100644 index f8fe865981..0000000000 --- a/packages/commons/fdr-utils/src/__test__/buildUrl.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { buildUrl } from "../buildUrl"; - -describe("buildUrl", () => { - it("should return the correct url", () => { - expect(buildUrl({ host: "mydocs.docs.buildwithfern.com", pathname: "path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); - - it("should correctly handle urls with 'https://'", () => { - expect(buildUrl({ host: "https://mydocs.docs.buildwithfern.com", pathname: "path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); - - it("should correctly handle urls with trailing slashes", () => { - expect(buildUrl({ host: "mydocs.docs.buildwithfern.com/", pathname: "path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); - - it("should correctly handle pathnames with leading slashes", () => { - expect(buildUrl({ host: "mydocs.docs.buildwithfern.com", pathname: "/path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); - - it("should correctly handle both 'https://' and trailing slashes at host", () => { - expect(buildUrl({ host: "https://mydocs.docs.buildwithfern.com/", pathname: "path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); - - it("should correctly handle 'https://', trailing slashes at host and leading slash at pathname", () => { - expect(buildUrl({ host: "https://mydocs.docs.buildwithfern.com/", pathname: "/path/to/page" })).toEqual( - "mydocs.docs.buildwithfern.com/path/to/page", - ); - }); -}); diff --git a/packages/commons/fdr-utils/src/buildUrl.ts b/packages/commons/fdr-utils/src/buildUrl.ts index a9e5800f79..ea1bceffa7 100644 --- a/packages/commons/fdr-utils/src/buildUrl.ts +++ b/packages/commons/fdr-utils/src/buildUrl.ts @@ -1,20 +1,8 @@ -// returns: `mydocs.docs.buildwithfern.com/path/to/page` -export function buildUrl({ host, pathname }: { host: string; pathname?: string }): string { - // Remove protocol if it exists - host = host.replace(/(^\w+:|^)\/\//, ""); - - // Remove trailing slash at the end of the host - host = host.endsWith("/") ? host.slice(0, -1) : host; - - // Remove '.staging' if it exists - host = host.replace(".docs.staging.", ".docs."); - - if (pathname == null || pathname.length === 0) { - return host; +export function buildUrl({ host, pathname }: { host: string; pathname: string }): string { + let hostWithoutTrailingSlash = host.endsWith("/") ? host.slice(0, -1) : host; + hostWithoutTrailingSlash = hostWithoutTrailingSlash.replace(".docs.staging.", ".docs."); + if (pathname.length === 0) { + return hostWithoutTrailingSlash; } - - // Remove leading slash at the start of the pathname - pathname = pathname.startsWith("/") ? pathname.slice(1) : pathname; - - return `${host}/${pathname}`; + return `${hostWithoutTrailingSlash}/${pathname}`; } diff --git a/packages/commons/fdr-utils/src/getAllUrlsFromDocsConfig.ts b/packages/commons/fdr-utils/src/getAllUrlsFromDocsConfig.ts index c1b28eadae..2bc3a870f0 100644 --- a/packages/commons/fdr-utils/src/getAllUrlsFromDocsConfig.ts +++ b/packages/commons/fdr-utils/src/getAllUrlsFromDocsConfig.ts @@ -8,15 +8,6 @@ export function getAllUrlsFromDocsConfig( basePath: string | undefined, nav: DocsV1Read.NavigationConfig, apis: Record, -): string[] { - const slugs = getAllSlugsFromDocsConfig(basePath, nav, apis); - return slugs.map((slug) => buildUrl({ host, pathname: slug })); -} - -export function getAllSlugsFromDocsConfig( - basePath: string | undefined, - nav: DocsV1Read.NavigationConfig, - apis: Record, ): string[] { const basePathSlug = basePath != null ? basePath.split("/").filter((t) => t.length > 0) : []; const root = resolveSidebarNodesRoot(nav, apis, basePathSlug); @@ -26,5 +17,5 @@ export function getAllSlugsFromDocsConfig( visitedSlugs.push(node.slug.join("/")); }); - return Array.from(new Set(visitedSlugs)); + return Array.from(new Set(visitedSlugs.map((slug) => buildUrl({ host, pathname: slug })))); } diff --git a/packages/ui/docs-bundle/package.json b/packages/ui/docs-bundle/package.json index 9f68ff0125..7c8aab1414 100644 --- a/packages/ui/docs-bundle/package.json +++ b/packages/ui/docs-bundle/package.json @@ -43,7 +43,6 @@ "@aws-sdk/s3-request-presigner": "^3.540.0", "@fern-api/fdr-sdk": "workspace:*", "@fern-api/venus-api-sdk": "^0.1.0", - "@fern-fern/revalidation-sdk": "0.0.6", "@fern-ui/core-utils": "workspace:*", "@fern-ui/fdr-utils": "workspace:*", "@fern-ui/ui": "workspace:*", diff --git a/packages/ui/docs-bundle/src/pages/404.tsx b/packages/ui/docs-bundle/src/pages/404.tsx index ebf179e282..960866ce86 100644 --- a/packages/ui/docs-bundle/src/pages/404.tsx +++ b/packages/ui/docs-bundle/src/pages/404.tsx @@ -24,8 +24,10 @@ export default NotFoundPage; export const getStaticProps: GetStaticProps = async ({ params = {} }) => { const host = params.host as string | undefined; + const slugArray = params.slug as string[] | undefined; + const pathname = slugArray != null ? slugArray.join("/") : ""; const docs = await REGISTRY_SERVICE.docs.v2.read.getDocsForUrl({ - url: process.env.NEXT_PUBLIC_DOCS_DOMAIN ?? buildUrl({ host: host ?? "" }), + url: process.env.NEXT_PUBLIC_DOCS_DOMAIN ?? buildUrl({ host: host ?? "", pathname }), }); if (!docs.ok) { diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all.ts index 739c483f12..0ace735fb1 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all.ts @@ -1,6 +1,7 @@ import { buildUrl, getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; import { loadWithUrl } from "../../../utils/loadWithUrl"; +import { toValidPathname } from "../../../utils/toValidPathname"; function getHostFromUrl(url: string | undefined): string | undefined { if (url == null) { @@ -54,8 +55,14 @@ const handler: NextApiHandler = async ( if (typeof xFernHost !== "string") { return res.status(404).json({ successfulRevalidations: [], failedRevalidations: [] }); } + const hostWithoutTrailingSlash = xFernHost.endsWith("/") ? xFernHost.slice(0, -1) : xFernHost; - const docs = await loadWithUrl(buildUrl({ host: xFernHost })); + const docs = await loadWithUrl( + buildUrl({ + host: hostWithoutTrailingSlash, + pathname: toValidPathname(req.query.basePath), + }), + ); if (docs == null) { // return notFoundResponse(); diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all/v2.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all/v2.ts index 9a2ed5bce3..b7cb61fc67 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all/v2.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-all/v2.ts @@ -1,9 +1,8 @@ -import { FernRevalidation } from "@fern-fern/revalidation-sdk"; import { isPlainObject } from "@fern-ui/core-utils"; import { buildUrl, getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; import { loadWithUrl } from "../../../../utils/loadWithUrl"; -import { RevalidatePathResult, isFailureResult, isSuccessResult } from "../../../../utils/revalidate-types"; +import { toValidPathname } from "../../../../utils/toValidPathname"; function getHostFromUrl(url: string | undefined): string | undefined { if (url == null) { @@ -17,9 +16,35 @@ export const config = { maxDuration: 300, }; +type RevalidatePathResult = RevalidatePathSuccessResult | RevalidatePathErrorResult; + +interface RevalidatePathSuccessResult { + success: true; + url: string; +} + +function isSuccessResult(result: RevalidatePathResult): result is RevalidatePathSuccessResult { + return result.success; +} + +interface RevalidatePathErrorResult { + success: false; + url: string; + message: string; +} + +function isFailureResult(result: RevalidatePathResult): result is RevalidatePathErrorResult { + return !result.success; +} + +type RevalidatedPaths = { + successfulRevalidations: RevalidatePathSuccessResult[]; + failedRevalidations: RevalidatePathErrorResult[]; +}; + const handler: NextApiHandler = async ( req: NextApiRequest, - res: NextApiResponse, + res: NextApiResponse, ): Promise => { if (req.method !== "POST") { return res.status(405).json({ successfulRevalidations: [], failedRevalidations: [] }); @@ -31,8 +56,14 @@ const handler: NextApiHandler = async ( if (typeof xFernHost !== "string") { return res.status(404).json({ successfulRevalidations: [], failedRevalidations: [] }); } + const hostWithoutTrailingSlash = xFernHost.endsWith("/") ? xFernHost.slice(0, -1) : xFernHost; - const docs = await loadWithUrl(buildUrl({ host: xFernHost })); + const docs = await loadWithUrl( + buildUrl({ + host: hostWithoutTrailingSlash, + pathname: toValidPathname(req.query.basePath), + }), + ); if (docs == null) { // return notFoundResponse(); diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-bulk.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-bulk.ts deleted file mode 100644 index 32277ba0f6..0000000000 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/revalidate-bulk.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { FernRevalidation } from "@fern-fern/revalidation-sdk"; -import { isPlainObject } from "@fern-ui/core-utils"; -import { buildUrl } from "@fern-ui/fdr-utils"; -import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; -import { RevalidatePathResult, isFailureResult, isSuccessResult } from "../../../utils/revalidate-types"; - -function getHostFromUrl(url: string | undefined): string | undefined { - if (url == null) { - return undefined; - } - const urlObj = new URL(url); - return urlObj.host; -} - -export const config = { - maxDuration: 300, -}; - -const handler: NextApiHandler = async ( - req: NextApiRequest, - res: NextApiResponse, -): Promise => { - if (req.method !== "POST") { - return res.status(405).json({ successfulRevalidations: [], failedRevalidations: [] }); - } - try { - // when we call res.revalidate() nextjs uses - // req.headers.host to make the network request - const xFernHost = getHostFromBody(req.body) ?? req.headers["x-fern-host"] ?? getHostFromUrl(req.url); - const slugs = getSlugsFromBody(req.body); - if (typeof xFernHost !== "string") { - return res.status(404).json({ successfulRevalidations: [], failedRevalidations: [] }); - } - - const urls = slugs.map((slug) => buildUrl({ host: xFernHost, pathname: slug })); - - const results = await Promise.all( - urls.map(async (url): Promise => { - try { - await res.revalidate(`/static/${encodeURI(url)}`); - return { success: true, url }; - } catch (e) { - // eslint-disable-next-line no-console - console.error(e); - return { success: false, url, message: e instanceof Error ? e.message : "Unknown error." }; - } - }), - ); - - const successfulRevalidations = results.filter(isSuccessResult); - const failedRevalidations = results.filter(isFailureResult); - - return res - .status(failedRevalidations.length === 0 ? 200 : successfulRevalidations.length === 0 ? 500 : 207) - .json({ successfulRevalidations, failedRevalidations }); - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - return res.status(500).json({ successfulRevalidations: [], failedRevalidations: [] }); - } -}; - -export default handler; - -function getHostFromBody(body: unknown): string | undefined { - if (body == null || !isPlainObject(body)) { - return undefined; - } - - if (typeof body.host === "string") { - return body.host; - } - - return undefined; -} - -function getSlugsFromBody(body: unknown): string[] { - if (body == null || !isPlainObject(body)) { - return []; - } - - if (Array.isArray(body.slugs)) { - return body.slugs; - } - - return []; -} diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/list-slugs.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts similarity index 76% rename from packages/ui/docs-bundle/src/pages/api/fern-docs/list-slugs.ts rename to packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts index ace753c634..abb224b6d3 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/list-slugs.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts @@ -1,15 +1,10 @@ -import { buildUrl, getAllSlugsFromDocsConfig } from "@fern-ui/fdr-utils"; +import { buildUrl, getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextRequest, NextResponse } from "next/server"; import { loadWithUrl } from "../../../utils/loadWithUrl"; import { jsonResponse } from "../../../utils/serverResponse"; - -/** - * list-slugs generates all the slugs for the given host. - * Unlike sitemap.xml, this endpoint is used during revalidation, and should never be cached. - */ +import { toValidPathname } from "../../../utils/toValidPathname"; export const runtime = "edge"; -export const dynamic = "force-dynamic"; // force dynamic to avoid caching function getHostFromUrl(url: string | undefined): string | undefined { if (url == null) { @@ -42,19 +37,22 @@ export default async function GET(req: NextRequest): Promise { } try { - const docs = await loadWithUrl(buildUrl({ host: xFernHost })); + const docs = await loadWithUrl( + buildUrl({ host: xFernHost, pathname: toValidPathname(req.nextUrl.searchParams.get("basePath")) }), + ); if (docs == null) { return jsonResponse(404, [], headers); } - const slugs = getAllSlugsFromDocsConfig( + const urls = getAllUrlsFromDocsConfig( + xFernHost, docs.baseUrl.basePath, docs.definition.config.navigation, docs.definition.apis, ); - return jsonResponse(200, slugs, headers); + return jsonResponse(200, urls, headers); } catch (err) { // eslint-disable-next-line no-console console.error(err); diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts index aade4aa421..e10ae502f3 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts @@ -1,15 +1,8 @@ -import { buildUrl, getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextRequest, NextResponse } from "next/server"; -import { loadWithUrl } from "../../../utils/loadWithUrl"; -import { jsonResponse, notFoundResponse } from "../../../utils/serverResponse"; - -/** - * sitemap.xml generates a sitemap for the given host. This endpoint is used by search engines to index the site. - * We cache this response for 24 hours to avoid unnecessary load on the server. Since SEO is slow to update, this is acceptable. - */ +import { notFoundResponse } from "../../../utils/serverResponse"; export const runtime = "edge"; -export const revalidate = 60 * 60 * 24; // 24 hours +export const revalidate = 60 * 60 * 24; function getHostFromUrl(url: string | undefined): string | undefined { if (url == null) { @@ -20,28 +13,29 @@ function getHostFromUrl(url: string | undefined): string | undefined { } export default async function GET(req: NextRequest): Promise { - let xFernHost = req.headers.get("x-fern-host") ?? getHostFromUrl(req.nextUrl.href); - - if (xFernHost?.includes("localhost")) { - xFernHost = process.env.NEXT_PUBLIC_DOCS_DOMAIN; - } + const xFernHost = req.headers.get("x-fern-host") ?? getHostFromUrl(req.nextUrl.href); if (xFernHost == null || Array.isArray(xFernHost)) { return notFoundResponse(); } - const docs = await loadWithUrl(buildUrl({ host: xFernHost })); + const hostWithoutTrailingSlash = xFernHost.endsWith("/") ? xFernHost.slice(0, -1) : xFernHost; + + const hostnameAndProtocol = req.nextUrl.host.includes("localhost") + ? "http://localhost:3000" + : `https://${hostWithoutTrailingSlash}`; - if (docs == null) { - return jsonResponse(404, []); + const sitemapResponse = await fetch(`${hostnameAndProtocol}/api/fern-docs/sitemap`, { + headers: { "x-fern-host": xFernHost }, + }); + + if (sitemapResponse.status !== 200) { + // eslint-disable-next-line no-console + console.error("Failed to fetch docs", sitemapResponse.status, sitemapResponse.statusText); + return notFoundResponse(); } - const urls = getAllUrlsFromDocsConfig( - xFernHost, - docs.baseUrl.basePath, - docs.definition.config.navigation, - docs.definition.apis, - ); + const urls: string[] = await sitemapResponse.json(); const sitemap = getSitemapXml(urls.map((url) => `https://${url}`)); diff --git a/packages/ui/docs-bundle/src/utils/getDocsPageProps.ts b/packages/ui/docs-bundle/src/utils/getDocsPageProps.ts index 008ab882a6..8261b0f189 100644 --- a/packages/ui/docs-bundle/src/utils/getDocsPageProps.ts +++ b/packages/ui/docs-bundle/src/utils/getDocsPageProps.ts @@ -46,7 +46,8 @@ export async function getDocsPageProps( return { type: "notFound", notFound: true }; } - const url = buildUrl({ host: xFernHost }); + const pathname = decodeURI(slug != null ? slug.join("/") : ""); + const url = buildUrl({ host: xFernHost, pathname }); const docs = await REGISTRY_SERVICE.docs.v2.read.getDocsForUrl({ url }); if (!docs.ok) { if ((docs.error as any).content.statusCode === 401) { @@ -89,7 +90,7 @@ export async function getPrivateDocsPageProps( const registryService = getRegistryServiceWithToken(`workos_${token}`); - const url = buildUrl({ host: xFernHost }); + const url = buildUrl({ host: xFernHost, pathname: slug.join("/") }); const docs = await registryService.docs.v2.read.getPrivateDocsForUrl({ url }); if (!docs.ok) { diff --git a/packages/ui/docs-bundle/src/utils/revalidate-types.ts b/packages/ui/docs-bundle/src/utils/revalidate-types.ts deleted file mode 100644 index 52f2501096..0000000000 --- a/packages/ui/docs-bundle/src/utils/revalidate-types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FernRevalidation } from "@fern-fern/revalidation-sdk"; - -export type RevalidatePathResult = FernRevalidation.SuccessfulRevalidation | FernRevalidation.FailedRevalidation; - -export function isSuccessResult(result: RevalidatePathResult): result is FernRevalidation.SuccessfulRevalidation { - return result.success; -} - -export function isFailureResult(result: RevalidatePathResult): result is FernRevalidation.FailedRevalidation { - return !result.success; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3140e2b10..6055d74f72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -976,9 +976,6 @@ importers: '@fern-api/venus-api-sdk': specifier: ^0.1.0 version: 0.1.1 - '@fern-fern/revalidation-sdk': - specifier: 0.0.6 - version: 0.0.6 '@fern-ui/core-utils': specifier: workspace:* version: link:../../commons/core-utils @@ -1265,8 +1262,8 @@ importers: specifier: ^0.1.0 version: 0.1.1 '@fern-fern/revalidation-sdk': - specifier: 0.0.6 - version: 0.0.6 + specifier: 0.0.2 + version: 0.0.2 '@prisma/client': specifier: 5.11.0 version: 5.11.0(prisma@5.11.0) @@ -4143,8 +4140,8 @@ packages: - debug dev: false - /@fern-fern/revalidation-sdk@0.0.6: - resolution: {integrity: sha512-CgWwoAa9yREBq2Qo79ihLnV1jqYiPGRnYZW7wvwa0wuzNluy+Zjg93uNN/UsUAp4UDv2Ee+2jkKEA0XwXb/mmQ==} + /@fern-fern/revalidation-sdk@0.0.2: + resolution: {integrity: sha512-lmPGacKOaF/ZOolnfOBTNI03PQ20Im+73edWa8MIfMAfw6rKlkv0R++COa7f7hdar3/dI9LocABub9sO/3ehiA==} dependencies: form-data: 4.0.0 node-fetch: 2.7.0 diff --git a/servers/fdr/package.json b/servers/fdr/package.json index 69dd303e9d..e072a7b9da 100644 --- a/servers/fdr/package.json +++ b/servers/fdr/package.json @@ -14,7 +14,7 @@ "@aws-sdk/s3-request-presigner": "^3.540.0", "@fern-api/fdr-sdk": "workspace:*", "@fern-api/venus-api-sdk": "^0.1.0", - "@fern-fern/revalidation-sdk": "0.0.6", + "@fern-fern/revalidation-sdk": "0.0.2", "@prisma/client": "5.11.0", "@slack/web-api": "^6.9.0", "@ungap/url-search-params": "0.2.2", diff --git a/servers/fdr/src/__test__/ete/ete.test.ts b/servers/fdr/src/__test__/ete/ete.test.ts index 3af8664d7d..ea21f7b837 100644 --- a/servers/fdr/src/__test__/ete/ete.test.ts +++ b/servers/fdr/src/__test__/ete/ete.test.ts @@ -16,7 +16,7 @@ it("revalidates a custom docs domain", async () => { const revalidationResult = await revalidationService.revalidate({ baseUrl: ParsedBaseUrl.parse("https://fdr-ete-test.buildwithfern.com"), - // fernUrl: ParsedBaseUrl.parse("https://fdr-ete-test.buildwithfern.com"), + fernUrl: ParsedBaseUrl.parse("https://fdr-ete-test.buildwithfern.com"), }); expect(revalidationResult.revalidationFailed).toEqual(false); diff --git a/servers/fdr/src/controllers/docs/v2/getDocsWriteV2Service.ts b/servers/fdr/src/controllers/docs/v2/getDocsWriteV2Service.ts index 0d7d578b25..ae93dc2464 100644 --- a/servers/fdr/src/controllers/docs/v2/getDocsWriteV2Service.ts +++ b/servers/fdr/src/controllers/docs/v2/getDocsWriteV2Service.ts @@ -4,7 +4,6 @@ import { v4 as uuidv4 } from "uuid"; import { APIV1Db, DocsV1Write, DocsV2Write, DocsV2WriteService, FdrAPI } from "../../../api"; import { type FdrApplication } from "../../../app"; import { IndexSegment } from "../../../services/algolia"; -import { provideRevalidationClient } from "../../../services/revalidator/provideRevalidationClient"; import { type S3FileInfo } from "../../../services/s3"; import { WithoutQuestionMarks } from "../../../util"; import { ParsedBaseUrl } from "../../../util/ParsedBaseUrl"; @@ -139,16 +138,6 @@ export function getDocsWriteV2Service(app: FdrApplication): DocsV2WriteService { orgId: docsRegistrationInfo.orgId, }); - // get previous slugs, some of which need to be invalidated - let previousSlugs: string[] = []; - try { - const client = provideRevalidationClient(docsRegistrationInfo.fernUrl); - previousSlugs = await client.listSlugs(); - } catch (e) { - // this is not a critical error, so we just log it. It's possible that the domain has never been registered before, or is a private docs instance. - app.logger.warn(`Failed to get previous slugs for ${docsRegistrationInfo.fernUrl.getFullUrl()}`, e); - } - app.logger.debug(`[${docsRegistrationInfo.fernUrl.getFullUrl()}] Transforming Docs Definition to DB`); const dbDocsDefinition = convertDocsDefinitionToDb({ writeShape: req.body.docsDefinition, @@ -199,10 +188,9 @@ export function getDocsWriteV2Service(app: FdrApplication): DocsV2WriteService { urls.map(async (baseUrl) => { const results = await app.services.revalidator.revalidate({ // treat staging URL as its own fernURL to handle basepath revalidation - // fernUrl: baseUrl !== stagingUrl ? docsRegistrationInfo.fernUrl : stagingUrl, + fernUrl: baseUrl !== stagingUrl ? docsRegistrationInfo.fernUrl : stagingUrl, baseUrl, app, - oldSlugs: previousSlugs, }); if ( results.response != null && diff --git a/servers/fdr/src/services/revalidator/RevalidatorService.ts b/servers/fdr/src/services/revalidator/RevalidatorService.ts index b492317d19..6bda65c2ba 100644 --- a/servers/fdr/src/services/revalidator/RevalidatorService.ts +++ b/servers/fdr/src/services/revalidator/RevalidatorService.ts @@ -1,21 +1,19 @@ -import { FernRevalidation } from "@fern-fern/revalidation-sdk"; +import { FernRevalidation, FernRevalidationClient } from "@fern-fern/revalidation-sdk"; import axios, { type AxiosInstance } from "axios"; import * as AxiosLogger from "axios-logger"; import { FdrApplication } from "../../app"; import { ParsedBaseUrl } from "../../util/ParsedBaseUrl"; -import { provideRevalidationClient } from "./provideRevalidationClient"; export type RevalidatedPathsResponse = { - response?: FernRevalidation.BulkRevalidateResponse; + response?: FernRevalidation.RevalidateAllV2Response; revalidationFailed: boolean; }; export interface RevalidatorService { revalidate(params: { - // fernUrl: ParsedBaseUrl; + fernUrl: ParsedBaseUrl; baseUrl: ParsedBaseUrl; app: FdrApplication; - oldSlugs?: string[]; }): Promise; } @@ -31,10 +29,10 @@ export class RevalidatorServiceImpl implements RevalidatorService { /** * NOTE on basepath revalidation: * - * As of ui@0.62.0 (https://github.com/fern-api/fern-platform/pull/572) basepath revalidation is directly supported. Example: - * POST https://custom-domain.com/path/api/fern-docs/revalidate-all/v2 where `https://custom-domain.com/path` is the full environment URL. - * - * Since custom-domain.com is a rewrite to org.docs.buildwithfern.com, there's one caveat to keep in mind: + * When the baseUrl.path is not null, this means we are revalidating a custom domain which has configured subpath rewriting. + * In this case, we don't have access to /api/revalidate-all and revalidation would fail. + * Instead, we can hit https://org.docs.buildwithfern.com/api/revalidate-all?basePath=/path where the x-fern-host is set to custom-domain.com. + * This works because custom-domain.com/path is rewritten to org.docs.buildwithfern.com/path * * Example prefetch request: * https://custom-domain.com/path/_next/data/.../static/custom-domain.com/path.json is rewritten to: @@ -44,29 +42,28 @@ export class RevalidatorServiceImpl implements RevalidatorService { */ public async revalidate({ - // fernUrl, + fernUrl, baseUrl, app, - oldSlugs = [], }: { - // fernUrl: ParsedBaseUrl; + fernUrl: ParsedBaseUrl; baseUrl: ParsedBaseUrl; app?: FdrApplication; - oldSlugs?: string[]; }): Promise { let revalidationFailed = false; try { - const client = provideRevalidationClient(baseUrl); - - const newSlugs = await client.listSlugs({ xFernHost: baseUrl.hostname }); - - // we need to revalidate all paths that have ever been generated, before and after docs registration: - const slugs = Array.from(new Set([...oldSlugs, ...newSlugs])); - const response = await client.bulkRevalidate({ host: baseUrl.hostname, slugs }); - + const client = new FernRevalidationClient({ + environment: baseUrl.path != null ? `https://${fernUrl.hostname}` : `https://${baseUrl.hostname}`, + }); + console.log(baseUrl.path != null ? fernUrl.hostname : baseUrl.hostname); + const response = await client.revalidateAllV2({ + host: baseUrl.hostname, + basePath: baseUrl.path != null ? `?basePath=${baseUrl.path}` : "", + xFernHost: baseUrl.hostname, + }); return { response, - revalidationFailed, + revalidationFailed: false, }; } catch (e) { app?.logger.error("Failed to revalidate paths", e); @@ -74,7 +71,7 @@ export class RevalidatorServiceImpl implements RevalidatorService { console.log(e); return { response: undefined, - revalidationFailed, + revalidationFailed: true, }; } } diff --git a/servers/fdr/src/services/revalidator/provideRevalidationClient.ts b/servers/fdr/src/services/revalidator/provideRevalidationClient.ts deleted file mode 100644 index d1693bf5ef..0000000000 --- a/servers/fdr/src/services/revalidator/provideRevalidationClient.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { FernRevalidationClient } from "@fern-fern/revalidation-sdk"; -import { ParsedBaseUrl } from "../../util/ParsedBaseUrl"; - -export function provideRevalidationClient(baseUrl: ParsedBaseUrl): FernRevalidationClient { - // create a URL with the basepath - let hostWithBasepath = new URL(baseUrl.path ?? "/", `https://${baseUrl.hostname}`).href; - - // remove trailing slash - hostWithBasepath = hostWithBasepath.replace(/\/$/, ""); - - // create a client with the basepath - return new FernRevalidationClient({ environment: hostWithBasepath }); -} diff --git a/tests/fern/fern/docs.yml b/tests/fern/fern/docs.yml index e67893b8b1..1e53f9eaf2 100644 --- a/tests/fern/fern/docs.yml +++ b/tests/fern/fern/docs.yml @@ -33,7 +33,6 @@ navigation: path: ./content/overview/home/getting-started.mdx - page: Components path: ./content/components.mdx - # hidden: true - section: OpenAPI contents: - page: Importing OpenAPI