Skip to content

Commit

Permalink
Update server choice based on selection
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettJephson committed Sep 12, 2024
1 parent ec3602b commit cde6f2e
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 49 deletions.
Binary file modified bun.lockb
Binary file not shown.
13 changes: 7 additions & 6 deletions packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -101,22 +102,22 @@ 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)) {
result[key] = selection;
}
return result;
},
{ servers: serverIndex },
{ server: serverIndex },
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,7 @@
.openapi-markdown > *:last-child {
@apply mb-0;
}

.openapi-server-button {
@apply disabled:opacity-5;
}
1 change: 0 additions & 1 deletion packages/react-openapi/src/OpenAPICodeSample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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) + data.path,
method: data.method,
Expand Down
20 changes: 2 additions & 18 deletions packages/react-openapi/src/OpenAPIOperation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}>
Expand All @@ -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>
Expand All @@ -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) }],
},
},
};
}
15 changes: 9 additions & 6 deletions packages/react-openapi/src/OpenAPIServerURL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@ 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.
*/
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]) {
Expand All @@ -35,7 +37,7 @@ export function OpenAPIServerURL(props: {
/>
);
}
})}
})}{path}
</ServerURLForm>
);
}
Expand All @@ -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
Expand Down
32 changes: 21 additions & 11 deletions packages/react-openapi/src/OpenAPIServerURLForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]}`);
Expand All @@ -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>
);
Expand Down
8 changes: 4 additions & 4 deletions packages/react-openapi/src/ScalarApiButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}>
Expand Down
26 changes: 26 additions & 0 deletions packages/react-openapi/src/ServerSelector.tsx
Original file line number Diff line number Diff line change
@@ -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>;
}
11 changes: 8 additions & 3 deletions packages/react-openapi/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
id?: string;

blockKey?: string;
/**
* Optional id attached to the OpenAPI Operation heading and used as an anchor
*/
id?: string;

/**
* Selectors to update openapi enums, e.g. for server url variables
*/
enumSelectors?: Record<string, number>;
}

Expand Down

0 comments on commit cde6f2e

Please sign in to comment.