mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: support servers override
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
support servers override
|
||||
@@ -58,7 +58,7 @@
|
||||
"scripts": {
|
||||
"build": "tsdown --config-loader unrun",
|
||||
"clean": "rimraf dist",
|
||||
"dev": "tsdown --watch",
|
||||
"dev": "tsdown --watch --clean false",
|
||||
"lint": "eslint .",
|
||||
"types:check": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
type ComponentProps,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useApiContext } from '@/ui/contexts/api';
|
||||
import { useApiContext, useServerContext } from '@/ui/contexts/api';
|
||||
import type { FetchResult } from '@/playground/fetcher';
|
||||
import type { ParameterField, SecurityEntry } from '@/playground/index';
|
||||
import { getStatusInfo } from './status-info';
|
||||
@@ -142,7 +142,6 @@ export default function PlaygroundClient({
|
||||
const storageKeys = useStorageKey();
|
||||
const {
|
||||
mediaAdapters,
|
||||
serverRef,
|
||||
client: {
|
||||
playground: {
|
||||
components: { ResultDisplay = DefaultResultDisplay } = {},
|
||||
@@ -151,6 +150,7 @@ export default function PlaygroundClient({
|
||||
} = {},
|
||||
},
|
||||
} = useApiContext();
|
||||
const { serverRef } = useServerContext();
|
||||
const [securityId, setSecurityId] = useState(0);
|
||||
const { inputs, mapInputs, initAuthValues } = useAuthInputs(
|
||||
securities[securityId],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client';
|
||||
import { useApiContext, useServerSelectContext } from '@/ui/contexts/api';
|
||||
import { useServerContext, useServerSelectContext } from '@/ui/contexts/api';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -24,7 +24,7 @@ import type { NoReference } from '@/utils/schema';
|
||||
import { StfProvider, useFieldValue, useListener, useStf } from '@fumari/stf';
|
||||
|
||||
export default function ServerSelect(props: HTMLAttributes<HTMLDivElement>) {
|
||||
const { servers } = useApiContext();
|
||||
const { servers } = useServerContext();
|
||||
const { server, setServer, setServerVariables } = useServerSelectContext();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
@@ -29,7 +29,6 @@ export type MethodInformation = NoReference<OperationObject> & {
|
||||
|
||||
export interface RenderContext
|
||||
extends Pick<OpenAPIOptions, 'proxyUrl'>, Omit<CreateAPIPageOptions, 'renderMarkdown'> {
|
||||
servers: NoReference<ServerObject>[];
|
||||
slugger: Slugger;
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { RenderContext } from '@/types';
|
||||
import { createMethod } from '@/utils/schema';
|
||||
import type { OpenAPIV3_1 } from 'openapi-types';
|
||||
import type { ProcessedDocument } from '@/utils/process-document';
|
||||
import { ApiProviderLazy } from './contexts/api.lazy';
|
||||
import { ApiProviderLazy, ServerProviderLazy } from './contexts/api.lazy';
|
||||
|
||||
export interface ApiPageProps {
|
||||
document: Promise<ProcessedDocument> | string | ProcessedDocument;
|
||||
@@ -113,14 +113,12 @@ export async function APIPage({
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
let servers = ctx.schema.dereferenced.servers;
|
||||
if (!servers || servers.length === 0) servers = [{ url: '/' }];
|
||||
|
||||
return (
|
||||
<ApiProviderLazy
|
||||
servers={ctx.servers}
|
||||
shikiOptions={ctx.shikiOptions}
|
||||
client={ctx.client ?? {}}
|
||||
>
|
||||
{content}
|
||||
<ApiProviderLazy shikiOptions={ctx.shikiOptions} client={ctx.client ?? {}}>
|
||||
<ServerProviderLazy servers={servers}>{content}</ServerProviderLazy>
|
||||
</ApiProviderLazy>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -211,19 +211,12 @@ export function createAPIPage(
|
||||
processed = await document;
|
||||
}
|
||||
|
||||
const { dereferenced } = processed;
|
||||
const servers =
|
||||
dereferenced.servers && dereferenced.servers.length > 0
|
||||
? dereferenced.servers
|
||||
: [{ url: '/' }];
|
||||
|
||||
const slugger = new Slugger();
|
||||
|
||||
const ctx: RenderContext = {
|
||||
schema: processed,
|
||||
proxyUrl: server.options.proxyUrl,
|
||||
...options,
|
||||
servers,
|
||||
mediaAdapters: {
|
||||
...defaultAdapters,
|
||||
...options.mediaAdapters,
|
||||
|
||||
@@ -4,3 +4,7 @@ import { wrapLazy } from '../../utils/lazy';
|
||||
export const ApiProviderLazy = wrapLazy(() =>
|
||||
import('./api').then((mod) => ({ default: mod.ApiProvider })),
|
||||
);
|
||||
|
||||
export const ServerProviderLazy = wrapLazy(() =>
|
||||
import('./api').then((mod) => ({ default: mod.ServerProvider })),
|
||||
);
|
||||
|
||||
@@ -13,28 +13,38 @@ import type { RenderContext, ServerObject } from '@/types';
|
||||
import { defaultAdapters, type MediaAdapter } from '@/requests/media/adapter';
|
||||
import type { NoReference } from '@/utils/schema';
|
||||
import { useStorageKey } from '../client/storage-key';
|
||||
import type { APIPageClientOptions } from '../client';
|
||||
|
||||
type InheritFromContext = Pick<Required<RenderContext>, 'servers' | 'client'> &
|
||||
Pick<RenderContext, 'shikiOptions'>;
|
||||
interface InheritFromContext extends Pick<RenderContext, 'shikiOptions'> {
|
||||
client: APIPageClientOptions;
|
||||
}
|
||||
|
||||
export interface ApiProviderProps extends InheritFromContext {
|
||||
export interface ServerProviderProps {
|
||||
/**
|
||||
* Base URL for API requests
|
||||
*/
|
||||
defaultBaseUrl?: string;
|
||||
|
||||
servers: NoReference<ServerObject>[];
|
||||
}
|
||||
|
||||
interface ServerContextType extends ServerProviderProps {
|
||||
/**
|
||||
* ref to selected API server (to query)
|
||||
*/
|
||||
serverRef: RefObject<SelectedServer | null>;
|
||||
}
|
||||
|
||||
const ServerContext = createContext<ServerContextType | null>(null);
|
||||
|
||||
export type ApiProviderProps = InheritFromContext;
|
||||
|
||||
export interface SelectedServer {
|
||||
url: string;
|
||||
variables: Record<string, string>;
|
||||
}
|
||||
|
||||
interface ApiContextType extends InheritFromContext {
|
||||
/**
|
||||
* ref to selected API server (to query)
|
||||
*/
|
||||
serverRef: RefObject<SelectedServer | null>;
|
||||
|
||||
mediaAdapters: Record<string, MediaAdapter>;
|
||||
}
|
||||
|
||||
@@ -54,6 +64,10 @@ export function useApiContext(): ApiContextType {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function useServerContext() {
|
||||
return use(ServerContext)!;
|
||||
}
|
||||
|
||||
export function useServerSelectContext(): ServerSelectType {
|
||||
const ctx = use(ServerSelectContext);
|
||||
if (!ctx) throw new Error('Component must be used under <ApiProvider />');
|
||||
@@ -62,42 +76,50 @@ export function useServerSelectContext(): ServerSelectType {
|
||||
}
|
||||
|
||||
export function ApiProvider({
|
||||
defaultBaseUrl,
|
||||
children,
|
||||
servers,
|
||||
shikiOptions,
|
||||
client,
|
||||
}: ApiProviderProps & { children: ReactNode }) {
|
||||
const serverRef = useRef<SelectedServer | null>(null);
|
||||
|
||||
return (
|
||||
<ApiContext
|
||||
value={useMemo(
|
||||
() => ({
|
||||
serverRef,
|
||||
shikiOptions,
|
||||
client,
|
||||
mediaAdapters: {
|
||||
...defaultAdapters,
|
||||
...client.mediaAdapters,
|
||||
},
|
||||
servers,
|
||||
}),
|
||||
[servers, client, shikiOptions],
|
||||
[client, shikiOptions],
|
||||
)}
|
||||
>
|
||||
<ServerSelectProvider defaultBaseUrl={defaultBaseUrl}>{children}</ServerSelectProvider>
|
||||
{children}
|
||||
</ApiContext>
|
||||
);
|
||||
}
|
||||
|
||||
export function ServerProvider({
|
||||
servers,
|
||||
defaultBaseUrl,
|
||||
children,
|
||||
}: ServerProviderProps & { children: ReactNode }) {
|
||||
const serverRef = useRef<SelectedServer | null>(null);
|
||||
|
||||
return (
|
||||
<ServerContext value={useMemo(() => ({ servers, serverRef }), [servers])}>
|
||||
<ServerSelectProvider defaultBaseUrl={defaultBaseUrl}>{children}</ServerSelectProvider>
|
||||
</ServerContext>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerSelectProvider({
|
||||
defaultBaseUrl,
|
||||
children,
|
||||
}: Pick<ApiProviderProps, 'defaultBaseUrl'> & {
|
||||
}: Pick<ServerProviderProps, 'defaultBaseUrl'> & {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const { servers, serverRef } = useApiContext();
|
||||
const { servers, serverRef } = use(ServerContext)!;
|
||||
const storageKeys = useStorageKey();
|
||||
const [server, setServer] = useState<SelectedServer | null>(() => {
|
||||
const defaultItem = defaultBaseUrl
|
||||
|
||||
@@ -23,6 +23,7 @@ import { cn } from '@/utils/cn';
|
||||
import { APIPlayground } from '@/playground';
|
||||
import { getExampleRequests, RequestTabs } from './request-tabs';
|
||||
import { UsageTabsProviderLazy } from './usage-tabs/lazy';
|
||||
import { ServerProviderLazy } from '../contexts/api.lazy';
|
||||
|
||||
const ParamTypes = {
|
||||
path: 'Path Parameters',
|
||||
@@ -263,7 +264,7 @@ export async function Operation({
|
||||
};
|
||||
|
||||
const playgroundEnabled = ctx.playground?.enabled ?? true;
|
||||
const content = await renderOperationLayout(
|
||||
let content = await renderOperationLayout(
|
||||
{
|
||||
header: headNode,
|
||||
description: descriptionNode,
|
||||
@@ -288,7 +289,7 @@ export async function Operation({
|
||||
method,
|
||||
);
|
||||
|
||||
return (
|
||||
content = (
|
||||
<UsageTabsProviderLazy
|
||||
defaultExampleId={method['x-exclusiveCodeSample'] ?? method['x-selectedCodeSample']}
|
||||
route={path}
|
||||
@@ -297,6 +298,11 @@ export async function Operation({
|
||||
{content}
|
||||
</UsageTabsProviderLazy>
|
||||
);
|
||||
if (method.servers) {
|
||||
content = <ServerProviderLazy servers={method.servers}>{content}</ServerProviderLazy>;
|
||||
}
|
||||
|
||||
return content;
|
||||
} else {
|
||||
renderWebhookLayout ??= (slots) => (
|
||||
<div className="flex flex-col-reverse gap-x-6 gap-y-4 @4xl:flex-row @4xl:items-start">
|
||||
|
||||
@@ -54,6 +54,7 @@ export function createMethod(
|
||||
description: path.description,
|
||||
summary: path.summary,
|
||||
...operation,
|
||||
servers: operation.servers ?? path.servers,
|
||||
parameters: [...(operation.parameters ?? []), ...(path.parameters ?? [])],
|
||||
method: method.toUpperCase(),
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ export async function renderContextFrom(input: string): Promise<RenderContext> {
|
||||
return text;
|
||||
},
|
||||
schema,
|
||||
servers: schema.dereferenced.servers!,
|
||||
slugger: new Slugger(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user