Skip to content

Commit

Permalink
fix: API playground should work when using custom domain basepath (#572)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Mar 25, 2024
1 parent 13a0ddc commit 8d740f0
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 32 deletions.
19 changes: 10 additions & 9 deletions packages/ui/app/src/api-playground/PlaygroundEndpoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { joinUrlSlugs } from "@fern-ui/fdr-utils";
import { failed, Loadable, loaded, loading, notStartedLoading } from "@fern-ui/loadable";
import { PaperPlaneIcon } from "@radix-ui/react-icons";
import { Dispatch, FC, ReactElement, SetStateAction, useCallback, useState } from "react";
import { resolve } from "url";
import { capturePosthogEvent } from "../analytics/posthog";
import { FernTooltipProvider } from "../components/FernTooltip";
import { useDocsContext } from "../contexts/docs-context/useDocsContext";
Expand Down Expand Up @@ -37,9 +38,9 @@ interface ProxyResponseWithMetadata {
metadata: ProxyResponse.SerializableResponse;
}

function executeProxy(req: ProxyRequest, useCdn: boolean): Promise<ProxyResponseWithMetadata> {
function executeProxy(req: ProxyRequest, basePath: string = ""): Promise<ProxyResponseWithMetadata> {
const startTime = performance.now();
return fetch((useCdn ? "https://app.buildwithfern.com" : "") + "/api/fern-docs/proxy", {
return fetch(resolve(basePath, "/api/fern-docs/proxy/rest"), {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -69,8 +70,8 @@ interface ResponseChunk {
time: number;
}

function executeProxyStream(req: ProxyRequest, useCdn: boolean): Promise<[Response, Stream<ResponseChunk>]> {
return fetch((useCdn ? "https://app.buildwithfern.com" : "") + "/api/fern-docs/proxy/stream", {
function executeProxyStream(req: ProxyRequest, basePath: string = ""): Promise<[Response, Stream<ResponseChunk>]> {
return fetch(resolve(basePath, "/api/fern-docs/proxy/stream"), {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -105,8 +106,8 @@ interface FileDownloadResponse {
size?: number;
}

async function executeFileDownload(req: ProxyRequest, useCdn: boolean): Promise<FileDownloadResponse> {
const r = await fetch((useCdn ? "https://app.buildwithfern.com" : "") + "/api/fern-docs/proxy/file", {
async function executeFileDownload(req: ProxyRequest, basePath: string = ""): Promise<FileDownloadResponse> {
const r = await fetch(resolve(basePath, "/api/fern-docs/proxy/file"), {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -161,7 +162,7 @@ export const PlaygroundEndpoint: FC<PlaygroundEndpointProps> = ({
body: await serializeFormStateBody(formState.body),
};
if (endpoint.responseBody?.shape.type === "stream") {
const [res, stream] = await executeProxyStream(req, basePath != null);
const [res, stream] = await executeProxyStream(req, basePath);
for await (const item of stream) {
setResponse((lastValue) =>
loaded({
Expand All @@ -178,7 +179,7 @@ export const PlaygroundEndpoint: FC<PlaygroundEndpointProps> = ({
);
}
} else if (endpoint.responseBody?.shape.type === "fileDownload") {
const res = await executeFileDownload(req, basePath != null);
const res = await executeFileDownload(req, basePath);
if (res.ok) {
setResponse(
loaded({
Expand All @@ -196,7 +197,7 @@ export const PlaygroundEndpoint: FC<PlaygroundEndpointProps> = ({
setResponse(failed(new Error(`Failed to download file: ${res.statusText}`)));
}
} else {
const loadedResponse = await executeProxy(req, basePath != null);
const loadedResponse = await executeProxy(req, basePath);
if (!loadedResponse.result.error) {
setResponse(loaded({ type: "json", ...loadedResponse.result }));
capturePosthogEvent("api_playground_request_received", {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/api-playground/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function stringifyFetch(
return `// ${endpoint.name} (${endpoint.method} ${endpoint.path
.map((part) => (part.type === "literal" ? part.value : `:${part.key}`))
.join("")})
const response = fetch("${buildEndpointUrl(endpoint, formState)}", {
const response = await fetch("${buildEndpointUrl(endpoint, formState)}", {
method: "${endpoint.method}",
headers: ${indentAfter(JSON.stringify(headers, undefined, 2), 2, 0)},${!isEmpty(body) ? `\n body: ${body},` : ""}
});
Expand Down
1 change: 1 addition & 0 deletions packages/ui/docs-bundle/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const nextConfig = {
* This rewrite rule will ensure that /base/path/_next/data/* is rewritten to /_next/data/* on the server
*/
{ source: "/:prefix*/_next/:path*", destination: "/_next/:path*" },
{ source: "/:prefix*/api/fern-docs/:path*", destination: "/api/fern-docs/:path*" },
/**
* Since we use cookie rewrites to determine if the path should be rewritten to /static or /dynamic, prefetch requests
* do not have access to these cookies, and will always be matched to /static. This rewrite rule will ensure that
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assertNever } from "@fern-ui/core-utils";
import type { ProxyRequest, ProxyResponse } from "@fern-ui/ui";
import { NextResponse, type NextRequest } from "next/server";
import { jsonResponse } from "../../../utils/serverResponse";
import { jsonResponse } from "../../../../utils/serverResponse";

export const runtime = "edge";
export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -88,6 +88,10 @@ export default async function POST(req: NextRequest): Promise<NextResponse> {
return new NextResponse(null, { status: 405 });
}
const startTime = Date.now();

// eslint-disable-next-line no-console
console.log("Starting proxy request to", req.url);

try {
const proxyRequest = (await req.json()) as ProxyRequest;
const headers = new Headers(proxyRequest.headers);
Expand All @@ -102,30 +106,44 @@ export default async function POST(req: NextRequest): Promise<NextResponse> {
headers,
body: await buildRequestBody(proxyRequest.body),
});

// eslint-disable-next-line no-console
console.log("Proxy request to", req.url, "completed with status", response.status);

let body = await response.text();
const endTime = Date.now();

// eslint-disable-next-line no-console
console.log("Proxy request to", req.url, "recieved response body after", endTime - startTime, "milliseconds");

try {
body = JSON.parse(body);
} catch (_e) {
// Ignore
} catch (e) {
// eslint-disable-next-line no-console
console.log("Failed to parse response body as JSON, but will return it as text.");
// eslint-disable-next-line no-console
console.error(e);
}
const endTime = Date.now();
const responseHeaders = response.headers;

return jsonResponse<ProxyResponse>(200, {
error: false,
response: {
headers: Object.fromEntries(responseHeaders.entries()),
ok: response.ok,
redirected: response.redirected,
status: response.status,
statusText: response.statusText,
type: response.type,
url: response.url,
body,
return NextResponse.json(
{
error: false,
response: {
headers: Object.fromEntries(responseHeaders.entries()),
ok: response.ok,
redirected: response.redirected,
status: response.status,
statusText: response.statusText,
type: response.type,
url: response.url,
body,
},
time: endTime - startTime,
size: responseHeaders.get("Content-Length"),
},
time: endTime - startTime,
size: responseHeaders.get("Content-Length"),
});
{ status: 200 },
);
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);
Expand Down
11 changes: 8 additions & 3 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:3000",
baseURL: "http://localhost:8080",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
Expand Down Expand Up @@ -72,8 +72,13 @@ export default defineConfig({

/* Run your local dev server before starting the tests */
webServer: {
command: "pnpm docs:dev",
url: "http://localhost:3000",
command: process.env.CI
? "NODE_ENV='production' pnpm docs:build && pnpm docs:start -- -p 8080"
: "NODE_ENV='development' pnpm docs:dev -- -p 8080",
url: "http://localhost:8080",
reuseExistingServer: !process.env.CI,
timeout: 60 * 1000 * 5, // 5 minutes
stdout: "pipe",
stderr: "pipe",
},
});
2 changes: 1 addition & 1 deletion playwright/proxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test("multipart-form upload", async ({ request }) => {
const base64Image = bitmap.toString("base64");
const mimeType = "image/jpeg";
const dataUrl = `data:${mimeType};base64,${base64Image}`;
const r = await request.post("http://localhost:3000/api/fern-docs/proxy", {
const r = await request.post("/api/fern-docs/proxy/rest", {
data: {
url: "https://api.hume.ai/v0/batch/jobs",
method: "POST",
Expand Down

0 comments on commit 8d740f0

Please sign in to comment.