mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 15:45:13 +00:00
Update server choice based on selection
This commit is contained in:
@@ -49,6 +49,7 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockSwagger>) {
|
||||
context.searchParams && context.searchParams.block === block.key
|
||||
? parseModifiers(data, context.searchParams)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<OpenAPIOperation
|
||||
data={data}
|
||||
@@ -101,14 +102,14 @@ function parseModifiers(data: OpenAPIOperationData, params: Record<string, strin
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const { servers } = params;
|
||||
const { server: serverQueryParam } = params;
|
||||
const serverIndex =
|
||||
servers && !isNaN(Number(servers))
|
||||
? Math.min(0, Math.max(Number(servers), servers.length - 1))
|
||||
serverQueryParam && !isNaN(Number(serverQueryParam))
|
||||
? Math.max(0, Math.min(Number(serverQueryParam), data.servers.length - 1))
|
||||
: 0;
|
||||
const server = data.servers[serverIndex];
|
||||
if (server && server.variables) {
|
||||
return Object.keys(server.variables).reduce<Record<string, number>>(
|
||||
if (server) {
|
||||
return Object.keys(server.variables ?? {}).reduce<Record<string, number>>(
|
||||
(result, key) => {
|
||||
const selection = Number(params[key]);
|
||||
if (!isNaN(selection)) {
|
||||
@@ -116,7 +117,7 @@ function parseModifiers(data: OpenAPIOperationData, params: Record<string, strin
|
||||
}
|
||||
return result;
|
||||
},
|
||||
{ servers: serverIndex },
|
||||
{ server: serverIndex },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,3 +364,7 @@
|
||||
.openapi-markdown > *:last-child {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
.openapi-server-button {
|
||||
@apply disabled:opacity-5;
|
||||
}
|
||||
@@ -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) +
|
||||
|
||||
@@ -28,9 +28,8 @@ export async function OpenAPIOperation(props: {
|
||||
enumSelectors: context.enumSelectors,
|
||||
};
|
||||
|
||||
const config = await getConfiguration(context);
|
||||
return (
|
||||
<ScalarApiClient>
|
||||
<ScalarApiClient serverUrl={getServersURL(data.servers, context.enumSelectors)}>
|
||||
<div className={classNames('openapi-operation', className)}>
|
||||
<div className="openapi-intro">
|
||||
<h2 className="openapi-summary" id={context.id}>
|
||||
@@ -49,8 +48,7 @@ export async function OpenAPIOperation(props: {
|
||||
{method.toUpperCase()}
|
||||
</span>
|
||||
<span className="openapi-url">
|
||||
<OpenAPIServerURL servers={servers} context={clientContext} />
|
||||
{path}
|
||||
<OpenAPIServerURL servers={servers} context={clientContext} path={path} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,17 +67,3 @@ export async function OpenAPIOperation(props: {
|
||||
</ScalarApiClient>
|
||||
);
|
||||
}
|
||||
|
||||
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) }],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<ServerURLForm context={context} server={server}>
|
||||
<ServerURLForm context={context} servers={servers} serverIndex={serverIndex}>
|
||||
{parts.map((part, i) => {
|
||||
if (part.kind === 'text') {
|
||||
if (part.kind === 'text') {
|
||||
return <span key={i}>{part.text}</span>;
|
||||
} else {
|
||||
if (!server.variables?.[part.name]) {
|
||||
@@ -35,7 +37,7 @@ export function OpenAPIServerURL(props: {
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
})}{path}
|
||||
</ServerURLForm>
|
||||
);
|
||||
}
|
||||
@@ -47,7 +49,8 @@ export function getServersURL(
|
||||
servers: OpenAPIV3.ServerObject[],
|
||||
selectors?: Record<string, number>,
|
||||
): 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
|
||||
|
||||
@@ -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<HTMLFormElement>(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 (
|
||||
<form action={updateServerUrl} className="contents">
|
||||
<form ref={formRef} action={updateServerVariables} className="contents">
|
||||
<fieldset disabled={isPending} className="contents">
|
||||
<input type="hidden" name="block" value={context.blockKey} />
|
||||
<span>{children}</span>
|
||||
{children}
|
||||
{ servers.length > 1 ? <ServerSelector servers={servers} currentIndex={serverIndex} onChange={switchServer} /> : null }
|
||||
</fieldset>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -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<null | {
|
||||
operationData: OpenAPIOperationData | null;
|
||||
@@ -125,12 +125,12 @@ export function ScalarApiClient(props: { children: React.ReactNode }) {
|
||||
headers: request.headers.map((header: Header) => {
|
||||
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 (
|
||||
<ScalarContext.Provider value={open}>
|
||||
|
||||
@@ -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 <span className="inline-flex pl-4 gap-2">
|
||||
<input type="hidden" value={`${index}`} name="server" />
|
||||
<button className="openapi-server-button" disabled={index === 0} onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIndex((index) => index - 1);
|
||||
}} type="button" aria-label="Previous">◀</button>
|
||||
<button className="openapi-server-button" disabled={index >= servers.length - 1} onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIndex((index) => index + 1);
|
||||
}} type="button" aria-label="Next">▶</button>
|
||||
</span>;
|
||||
}
|
||||
@@ -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<string, number>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user