Skip to content

Commit cde6f2e

Browse files
committed
Update server choice based on selection
1 parent ec3602b commit cde6f2e

File tree

10 files changed

+81
-49
lines changed

10 files changed

+81
-49
lines changed

Diff for: bun.lockb

32 Bytes
Binary file not shown.

Diff for: packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx

+7-6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockSwagger>) {
4949
context.searchParams && context.searchParams.block === block.key
5050
? parseModifiers(data, context.searchParams)
5151
: undefined;
52+
5253
return (
5354
<OpenAPIOperation
5455
data={data}
@@ -101,22 +102,22 @@ function parseModifiers(data: OpenAPIOperationData, params: Record<string, strin
101102
if (!data) {
102103
return;
103104
}
104-
const { servers } = params;
105+
const { server: serverQueryParam } = params;
105106
const serverIndex =
106-
servers && !isNaN(Number(servers))
107-
? Math.min(0, Math.max(Number(servers), servers.length - 1))
107+
serverQueryParam && !isNaN(Number(serverQueryParam))
108+
? Math.max(0, Math.min(Number(serverQueryParam), data.servers.length - 1))
108109
: 0;
109110
const server = data.servers[serverIndex];
110-
if (server && server.variables) {
111-
return Object.keys(server.variables).reduce<Record<string, number>>(
111+
if (server) {
112+
return Object.keys(server.variables ?? {}).reduce<Record<string, number>>(
112113
(result, key) => {
113114
const selection = Number(params[key]);
114115
if (!isNaN(selection)) {
115116
result[key] = selection;
116117
}
117118
return result;
118119
},
119-
{ servers: serverIndex },
120+
{ server: serverIndex },
120121
);
121122
}
122123
}

Diff for: packages/gitbook/src/components/DocumentView/OpenAPI/style.css

+4
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,7 @@
363363
.openapi-markdown > *:last-child {
364364
@apply mb-0;
365365
}
366+
367+
.openapi-server-button {
368+
@apply disabled:opacity-5;
369+
}

Diff for: packages/react-openapi/src/OpenAPICodeSample.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export function OpenAPICodeSample(props: {
3636

3737
const requestBody = noReference(data.operation.requestBody);
3838
const requestBodyContent = requestBody ? Object.entries(requestBody.content)[0] : undefined;
39-
4039
const input: CodeSampleInput = {
4140
url: getServersURL(data.servers, context.enumSelectors) + data.path,
4241
method: data.method,

Diff for: packages/react-openapi/src/OpenAPIOperation.tsx

+2-18
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ export async function OpenAPIOperation(props: {
2828
enumSelectors: context.enumSelectors,
2929
};
3030

31-
const config = await getConfiguration(context);
3231
return (
33-
<ScalarApiClient>
32+
<ScalarApiClient serverUrl={getServersURL(data.servers, context.enumSelectors)}>
3433
<div className={classNames('openapi-operation', className)}>
3534
<div className="openapi-intro">
3635
<h2 className="openapi-summary" id={context.id}>
@@ -49,8 +48,7 @@ export async function OpenAPIOperation(props: {
4948
{method.toUpperCase()}
5049
</span>
5150
<span className="openapi-url">
52-
<OpenAPIServerURL servers={servers} context={clientContext} />
53-
{path}
51+
<OpenAPIServerURL servers={servers} context={clientContext} path={path} />
5452
</span>
5553
</div>
5654
</div>
@@ -69,17 +67,3 @@ export async function OpenAPIOperation(props: {
6967
</ScalarApiClient>
7068
);
7169
}
72-
73-
async function getConfiguration(context: OpenAPIContextProps) {
74-
const response = await fetch(context.specUrl);
75-
const doc = await response.json();
76-
77-
return {
78-
spec: {
79-
content: {
80-
...doc,
81-
servers: [{ url: getServersURL(doc.servers, context.enumSelectors) }],
82-
},
83-
},
84-
};
85-
}

Diff for: packages/react-openapi/src/OpenAPIServerURL.tsx

+9-6
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,25 @@ import { OpenAPIV3 } from 'openapi-types';
33
import { OpenAPIServerURLVariable } from './OpenAPIServerURLVariable';
44
import { OpenAPIClientContext } from './types';
55
import { ServerURLForm } from './OpenAPIServerURLForm';
6+
import { ServerSelector } from './ServerSelector';
67

78
/**
89
* Show the url of the server with variables replaced by their default values.
910
*/
1011
export function OpenAPIServerURL(props: {
1112
servers: OpenAPIV3.ServerObject[];
1213
context: OpenAPIClientContext;
14+
path?: string;
1315
}) {
14-
const { servers, context } = props;
15-
const serverIndex = context.enumSelectors?.servers ?? 0;
16+
const { path, servers, context } = props;
17+
const serverIndex = context.enumSelectors?.server ?? 0;
1618
const server = servers[serverIndex];
1719
const parts = parseServerURL(server?.url ?? '');
1820

1921
return (
20-
<ServerURLForm context={context} server={server}>
22+
<ServerURLForm context={context} servers={servers} serverIndex={serverIndex}>
2123
{parts.map((part, i) => {
22-
if (part.kind === 'text') {
24+
if (part.kind === 'text') {
2325
return <span key={i}>{part.text}</span>;
2426
} else {
2527
if (!server.variables?.[part.name]) {
@@ -35,7 +37,7 @@ export function OpenAPIServerURL(props: {
3537
/>
3638
);
3739
}
38-
})}
40+
})}{path}
3941
</ServerURLForm>
4042
);
4143
}
@@ -47,7 +49,8 @@ export function getServersURL(
4749
servers: OpenAPIV3.ServerObject[],
4850
selectors?: Record<string, number>,
4951
): string {
50-
const server = servers[0];
52+
const serverIndex = selectors && !isNaN(selectors.server) ? Number(selectors.server) : 0;
53+
const server = servers[serverIndex];
5154
const parts = parseServerURL(server?.url ?? '');
5255

5356
return parts

Diff for: packages/react-openapi/src/OpenAPIServerURLForm.tsx

+21-11
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,34 @@ import * as React from 'react';
44
import { useRouter } from 'next/navigation';
55
import { OpenAPIClientContext } from './types';
66
import { OpenAPIV3 } from 'openapi-types';
7-
import { useApiClientModal } from '@scalar/api-client-react';
7+
import { ServerSelector } from './ServerSelector';
88

99
export function ServerURLForm(props: {
1010
children: React.ReactNode;
1111
context: OpenAPIClientContext;
12-
server: OpenAPIV3.ServerObject;
12+
servers: OpenAPIV3.ServerObject[];
13+
serverIndex: number;
1314
}) {
14-
const { children, context, server } = props;
15+
const { children, context, servers, serverIndex } = props;
1516
const router = useRouter();
16-
const client = useApiClientModal();
1717
const [isPending, startTransition] = React.useTransition();
18+
19+
const server = servers[serverIndex];
20+
const formRef = React.useRef<HTMLFormElement>(null);
1821

19-
function updateServerUrl(formData: FormData) {
22+
function switchServer(index: number) {
2023
startTransition(() => {
21-
if (!server.variables) {
22-
return;
24+
if (index !== serverIndex) {
25+
let params = new URLSearchParams(`block=${context.blockKey}&server=${index ?? '0'}`);
26+
router.push(`?${params}`, { scroll: false });
2327
}
24-
let params = new URLSearchParams(`block=${context.blockKey}`);
25-
const variableKeys = Object.keys(server.variables);
28+
});
29+
}
30+
31+
function updateServerVariables(formData: FormData) {
32+
startTransition(() => {
33+
let params = new URLSearchParams(`block=${context.blockKey}&server=${formData.get('server') ?? '0'}`);
34+
const variableKeys = Object.keys(server.variables ?? {});
2635
for (const pair of formData.entries()) {
2736
if (variableKeys.includes(pair[0]) && !isNaN(Number(pair[1]))) {
2837
params.set(pair[0], `${pair[1]}`);
@@ -33,10 +42,11 @@ export function ServerURLForm(props: {
3342
}
3443

3544
return (
36-
<form action={updateServerUrl} className="contents">
45+
<form ref={formRef} action={updateServerVariables} className="contents">
3746
<fieldset disabled={isPending} className="contents">
3847
<input type="hidden" name="block" value={context.blockKey} />
39-
<span>{children}</span>
48+
{children}
49+
{ servers.length > 1 ? <ServerSelector servers={servers} currentIndex={serverIndex} onChange={switchServer} /> : null }
4050
</fieldset>
4151
</form>
4252
);

Diff for: packages/react-openapi/src/ScalarApiButton.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ export function ScalarApiButton(props: {
5555
/**
5656
* Wrap the rendering with a context to open the scalar modal.
5757
*/
58-
export function ScalarApiClient(props: { children: React.ReactNode }) {
59-
const { children } = props;
58+
export function ScalarApiClient(props: { children: React.ReactNode; serverUrl?: string }) {
59+
const { children, serverUrl } = props;
6060

6161
const [active, setActive] = React.useState<null | {
6262
operationData: OpenAPIOperationData | null;
@@ -125,12 +125,12 @@ export function ScalarApiClient(props: { children: React.ReactNode }) {
125125
headers: request.headers.map((header: Header) => {
126126
return { ...header, enabled: true };
127127
}),
128-
url: operationData.servers[0]?.url,
128+
url: serverUrl ?? operationData.servers[0]?.url,
129129
body: request.postData?.text,
130130
};
131131

132132
return data;
133-
}, [active]);
133+
}, [active, serverUrl]);
134134

135135
return (
136136
<ScalarContext.Provider value={open}>

Diff for: packages/react-openapi/src/ServerSelector.tsx

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
5+
export function ServerSelector(props: { currentIndex: number; onChange: (value:number) => void; servers: any[] }) {
6+
const { currentIndex, onChange, servers } = props;
7+
const [index, setIndex] = React.useState(currentIndex);
8+
9+
React.useEffect(() => {
10+
onChange(index);
11+
}, [index]);
12+
13+
return <span className="inline-flex pl-4 gap-2">
14+
<input type="hidden" value={`${index}`} name="server" />
15+
<button className="openapi-server-button" disabled={index === 0} onClick={(e) => {
16+
e.preventDefault();
17+
e.stopPropagation();
18+
setIndex((index) => index - 1);
19+
}} type="button" aria-label="Previous"></button>
20+
<button className="openapi-server-button" disabled={index >= servers.length - 1} onClick={(e) => {
21+
e.preventDefault();
22+
e.stopPropagation();
23+
setIndex((index) => index + 1);
24+
}} type="button" aria-label="Next"></button>
25+
</span>;
26+
}

Diff for: packages/react-openapi/src/types.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@ export interface OpenAPIClientContext {
1515
* @default false
1616
*/
1717
defaultInteractiveOpened?: boolean;
18+
1819
/**
1920
* The key of the block
2021
*/
2122
blockKey?: string;
22-
/** Optional id attached to the OpenAPI Operation heading and used as an anchor */
23-
id?: string;
2423

25-
blockKey?: string;
24+
/**
25+
* Optional id attached to the OpenAPI Operation heading and used as an anchor
26+
*/
27+
id?: string;
2628

29+
/**
30+
* Selectors to update openapi enums, e.g. for server url variables
31+
*/
2732
enumSelectors?: Record<string, number>;
2833
}
2934

0 commit comments

Comments
 (0)