From 4f59d18bfc96070ca98ce732da6081003ef5af1c Mon Sep 17 00:00:00 2001 From: Brett Jephson Date: Thu, 12 Sep 2024 16:46:18 +0100 Subject: [PATCH] Update server choice based on selection --- .../DocumentView/OpenAPI/OpenAPI.tsx | 13 ++++---- .../components/DocumentView/OpenAPI/style.css | 4 +++ .../react-openapi/src/OpenAPICodeSample.tsx | 1 - .../react-openapi/src/OpenAPIOperation.tsx | 20 ++---------- .../react-openapi/src/OpenAPIServerURL.tsx | 15 +++++---- .../src/OpenAPIServerURLForm.tsx | 32 ++++++++++++------- .../react-openapi/src/ScalarApiButton.tsx | 8 ++--- packages/react-openapi/src/ServerSelector.tsx | 26 +++++++++++++++ packages/react-openapi/src/types.ts | 11 +++++-- 9 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 packages/react-openapi/src/ServerSelector.tsx diff --git a/packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx b/packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx index ee96a211e..438c3ca27 100644 --- a/packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx +++ b/packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx @@ -49,6 +49,7 @@ async function OpenAPIBody(props: BlockProps) { context.searchParams && context.searchParams.block === block.key ? parseModifiers(data, context.searchParams) : undefined; + return ( >( + if (server) { + return Object.keys(server.variables ?? {}).reduce>( (result, key) => { const selection = Number(params[key]); if (!isNaN(selection)) { @@ -116,7 +117,7 @@ function parseModifiers(data: OpenAPIOperationData, params: Record *:last-child { @apply mb-0; } + +.openapi-server-button { + @apply disabled:opacity-5; +} \ No newline at end of file diff --git a/packages/react-openapi/src/OpenAPICodeSample.tsx b/packages/react-openapi/src/OpenAPICodeSample.tsx index 670d2712d..eff0c9154 100644 --- a/packages/react-openapi/src/OpenAPICodeSample.tsx +++ b/packages/react-openapi/src/OpenAPICodeSample.tsx @@ -51,7 +51,6 @@ export function OpenAPICodeSample(props: { const requestBody = noReference(data.operation.requestBody); const requestBodyContent = requestBody ? Object.entries(requestBody.content)[0] : undefined; - const input: CodeSampleInput = { url: getServersURL(data.servers, context.enumSelectors) + diff --git a/packages/react-openapi/src/OpenAPIOperation.tsx b/packages/react-openapi/src/OpenAPIOperation.tsx index a6f495097..fb1daa951 100644 --- a/packages/react-openapi/src/OpenAPIOperation.tsx +++ b/packages/react-openapi/src/OpenAPIOperation.tsx @@ -28,9 +28,8 @@ export async function OpenAPIOperation(props: { enumSelectors: context.enumSelectors, }; - const config = await getConfiguration(context); return ( - +

@@ -49,8 +48,7 @@ export async function OpenAPIOperation(props: { {method.toUpperCase()} - - {path} +

@@ -69,17 +67,3 @@ export async function OpenAPIOperation(props: {
); } - -async function getConfiguration(context: OpenAPIContextProps) { - const response = await fetch(context.specUrl); - const doc = await response.json(); - - return { - spec: { - content: { - ...doc, - servers: [{ url: getServersURL(doc.servers, context.enumSelectors) }], - }, - }, - }; -} diff --git a/packages/react-openapi/src/OpenAPIServerURL.tsx b/packages/react-openapi/src/OpenAPIServerURL.tsx index 79973d621..c30517a9d 100644 --- a/packages/react-openapi/src/OpenAPIServerURL.tsx +++ b/packages/react-openapi/src/OpenAPIServerURL.tsx @@ -3,6 +3,7 @@ import { OpenAPIV3 } from 'openapi-types'; import { OpenAPIServerURLVariable } from './OpenAPIServerURLVariable'; import { OpenAPIClientContext } from './types'; import { ServerURLForm } from './OpenAPIServerURLForm'; +import { ServerSelector } from './ServerSelector'; /** * Show the url of the server with variables replaced by their default values. @@ -10,16 +11,17 @@ import { ServerURLForm } from './OpenAPIServerURLForm'; export function OpenAPIServerURL(props: { servers: OpenAPIV3.ServerObject[]; context: OpenAPIClientContext; + path?: string; }) { - const { servers, context } = props; - const serverIndex = context.enumSelectors?.servers ?? 0; + const { path, servers, context } = props; + const serverIndex = context.enumSelectors?.server ?? 0; const server = servers[serverIndex]; const parts = parseServerURL(server?.url ?? ''); return ( - + {parts.map((part, i) => { - if (part.kind === 'text') { + if (part.kind === 'text') { return {part.text}; } else { if (!server.variables?.[part.name]) { @@ -35,7 +37,7 @@ export function OpenAPIServerURL(props: { /> ); } - })} + })}{path} ); } @@ -47,7 +49,8 @@ export function getServersURL( servers: OpenAPIV3.ServerObject[], selectors?: Record, ): string { - const server = servers[0]; + const serverIndex = selectors && !isNaN(selectors.server) ? Number(selectors.server) : 0; + const server = servers[serverIndex]; const parts = parseServerURL(server?.url ?? ''); return parts diff --git a/packages/react-openapi/src/OpenAPIServerURLForm.tsx b/packages/react-openapi/src/OpenAPIServerURLForm.tsx index 565108417..2818bbbf6 100644 --- a/packages/react-openapi/src/OpenAPIServerURLForm.tsx +++ b/packages/react-openapi/src/OpenAPIServerURLForm.tsx @@ -4,25 +4,34 @@ import * as React from 'react'; import { useRouter } from 'next/navigation'; import { OpenAPIClientContext } from './types'; import { OpenAPIV3 } from 'openapi-types'; -import { useApiClientModal } from '@scalar/api-client-react'; +import { ServerSelector } from './ServerSelector'; export function ServerURLForm(props: { children: React.ReactNode; context: OpenAPIClientContext; - server: OpenAPIV3.ServerObject; + servers: OpenAPIV3.ServerObject[]; + serverIndex: number; }) { - const { children, context, server } = props; + const { children, context, servers, serverIndex } = props; const router = useRouter(); - const client = useApiClientModal(); const [isPending, startTransition] = React.useTransition(); + + const server = servers[serverIndex]; + const formRef = React.useRef(null); - function updateServerUrl(formData: FormData) { + function switchServer(index: number) { startTransition(() => { - if (!server.variables) { - return; + if (index !== serverIndex) { + let params = new URLSearchParams(`block=${context.blockKey}&server=${index ?? '0'}`); + router.push(`?${params}`, { scroll: false }); } - let params = new URLSearchParams(`block=${context.blockKey}`); - const variableKeys = Object.keys(server.variables); + }); + } + + function updateServerVariables(formData: FormData) { + startTransition(() => { + let params = new URLSearchParams(`block=${context.blockKey}&server=${formData.get('server') ?? '0'}`); + const variableKeys = Object.keys(server.variables ?? {}); for (const pair of formData.entries()) { if (variableKeys.includes(pair[0]) && !isNaN(Number(pair[1]))) { params.set(pair[0], `${pair[1]}`); @@ -33,10 +42,11 @@ export function ServerURLForm(props: { } return ( -
+
- {children} + {children} + { servers.length > 1 ? : null }
); diff --git a/packages/react-openapi/src/ScalarApiButton.tsx b/packages/react-openapi/src/ScalarApiButton.tsx index 02d9ffaa1..60b7f1586 100644 --- a/packages/react-openapi/src/ScalarApiButton.tsx +++ b/packages/react-openapi/src/ScalarApiButton.tsx @@ -55,8 +55,8 @@ export function ScalarApiButton(props: { /** * Wrap the rendering with a context to open the scalar modal. */ -export function ScalarApiClient(props: { children: React.ReactNode }) { - const { children } = props; +export function ScalarApiClient(props: { children: React.ReactNode; serverUrl?: string }) { + const { children, serverUrl } = props; const [active, setActive] = React.useState { return { ...header, enabled: true }; }), - url: operationData.servers[0]?.url, + url: serverUrl ?? operationData.servers[0]?.url, body: request.postData?.text, }; return data; - }, [active]); + }, [active, serverUrl]); return ( diff --git a/packages/react-openapi/src/ServerSelector.tsx b/packages/react-openapi/src/ServerSelector.tsx new file mode 100644 index 000000000..a235b7b67 --- /dev/null +++ b/packages/react-openapi/src/ServerSelector.tsx @@ -0,0 +1,26 @@ +'use client'; + +import * as React from 'react'; + +export function ServerSelector(props: { currentIndex: number; onChange: (value:number) => void; servers: any[] }) { + const { currentIndex, onChange, servers } = props; + const [index, setIndex] = React.useState(currentIndex); + + React.useEffect(() => { + onChange(index); + }, [index]); + + return + + + + ; +} \ No newline at end of file diff --git a/packages/react-openapi/src/types.ts b/packages/react-openapi/src/types.ts index b278f2efd..215d8ef56 100644 --- a/packages/react-openapi/src/types.ts +++ b/packages/react-openapi/src/types.ts @@ -15,15 +15,20 @@ export interface OpenAPIClientContext { * @default false */ defaultInteractiveOpened?: boolean; + /** * The key of the block */ blockKey?: string; - /** Optional id attached to the OpenAPI Operation heading and used as an anchor */ + + /** + * Optional id attached to the OpenAPI Operation heading and used as an anchor + */ id?: string; - blockKey?: string; - + /** + * Selectors to update openapi enums, e.g. for server url variables + */ enumSelectors?: Record; }