fumadocs-openapi
Version:
Generate MDX docs for your OpenAPI spec
157 lines (155 loc) • 5.8 kB
JavaScript
import { resolveRequestData } from "../../utils/url.js";
import { getPreferredType, pickExample } from "../../utils/schema.js";
import { MethodLabel } from "../components/method-label.js";
import { encodeRequestData } from "../../requests/media/encode.js";
import { AccordionContent, AccordionHeader, AccordionItem, AccordionTrigger, Accordions } from "../components/accordion.js";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "fumadocs-ui/components/tabs";
import { sample } from "openapi-sampler";
//#region src/ui/operation/request-tabs.tsx
function getExampleRequests(path, operation, ctx) {
const media = operation.requestBody ? getPreferredType(operation.requestBody.content) : null;
const bodyOfType = media ? operation.requestBody?.content[media] : null;
if (bodyOfType?.examples) {
const result = [];
for (const [key, value] of Object.entries(bodyOfType.examples)) {
const data$1 = getRequestData(path, operation, key, ctx);
result.push({
id: key,
name: value.summary || key,
description: value.description,
data: data$1,
encoded: encodeRequestData(data$1, ctx.mediaAdapters, operation.parameters ?? [])
});
}
if (result.length > 0) return result;
}
const data = getRequestData(path, operation, null, ctx);
return [{
id: "_default",
name: "Default",
description: bodyOfType?.schema?.description,
data,
encoded: encodeRequestData(data, ctx.mediaAdapters, operation.parameters ?? [])
}];
}
function getRequestData(path, method, sampleKey, _ctx) {
const result = {
path: {},
cookie: {},
header: {},
query: {},
method: method.method
};
for (const param of method.parameters ?? []) {
let value = pickExample(param);
if (value === void 0 && param.required) {
if (param.schema) value = sample(param.schema);
else if (param.content) {
const type = getPreferredType(param.content);
const content = type ? param.content[type] : void 0;
if (!content || !content.schema) throw new Error(`Cannot find "${param.name}" parameter info for media type "${type}" in ${path} ${method.method}`);
value = sample(content.schema);
}
}
switch (param.in) {
case "cookie":
result.cookie[param.name] = value;
break;
case "header":
result.header[param.name] = value;
break;
case "query":
result.query[param.name] = value;
break;
default: result.path[param.name] = value;
}
}
if (method.requestBody) {
const body = method.requestBody.content;
const type = getPreferredType(body);
if (!type) throw new Error(`Cannot find body schema for ${path} ${method.method}: missing media type`);
result.bodyMediaType = type;
const bodyOfType = body[type];
if (bodyOfType.examples && sampleKey) result.body = bodyOfType.examples[sampleKey].value;
else if (bodyOfType.example) result.body = bodyOfType.example;
else result.body = sample(bodyOfType?.schema ?? {}, {
skipReadOnly: method.method !== "GET",
skipWriteOnly: method.method === "GET",
skipNonRequired: true
});
}
return result;
}
async function RequestTabs({ path, operation, ctx }) {
if (!operation.requestBody) return null;
const { renderRequestTabs = renderRequestTabsDefault } = ctx.content ?? {};
return renderRequestTabs(getExampleRequests(path, operation, ctx), {
...ctx,
route: path,
operation
});
}
function renderRequestTabsDefault(items, ctx) {
function renderItem(item) {
const requestData = item.data;
const displayNames = {
body: /* @__PURE__ */ jsxs(Fragment, { children: ["Body", /* @__PURE__ */ jsx("code", {
className: "text-xs text-fd-muted-foreground ms-auto",
children: requestData.bodyMediaType
})] }),
cookie: "Cookie",
header: "Header",
query: "Query Parameters",
path: "Path Parameters"
};
return /* @__PURE__ */ jsxs(Fragment, { children: [
item.description && ctx.renderMarkdown(item.description),
/* @__PURE__ */ jsxs("div", {
className: "flex flex-row gap-2 items-center justify-between",
children: [/* @__PURE__ */ jsx(MethodLabel, { children: requestData.method }), /* @__PURE__ */ jsx("code", { children: resolveRequestData(ctx.route, item.encoded) })]
}),
/* @__PURE__ */ jsx(Accordions, {
type: "multiple",
className: "mt-2",
children: Object.entries(displayNames).map(([k, v]) => {
const data = requestData[k];
if (!data || Object.keys(data).length === 0) return;
return /* @__PURE__ */ jsxs(AccordionItem, {
value: k,
children: [/* @__PURE__ */ jsx(AccordionHeader, { children: /* @__PURE__ */ jsx(AccordionTrigger, { children: v }) }), /* @__PURE__ */ jsx(AccordionContent, {
className: "prose-no-margin",
children: ctx.renderCodeBlock("json", JSON.stringify(data, null, 2))
})]
}, k);
})
})
] });
}
let children;
if (items.length > 1) children = /* @__PURE__ */ jsxs(Tabs, {
defaultValue: items[0].id,
children: [/* @__PURE__ */ jsx(TabsList, { children: items.map((item) => /* @__PURE__ */ jsx(TabsTrigger, {
value: item.id,
children: item.name
}, item.id)) }), items.map((item) => /* @__PURE__ */ jsx(TabsContent, {
value: item.id,
children: renderItem(item)
}, item.id))]
});
else if (items.length === 1) children = renderItem(items[0]);
else children = /* @__PURE__ */ jsx("p", {
className: "text-fd-muted-foreground text-xs",
children: "Empty"
});
return /* @__PURE__ */ jsxs("div", {
className: "p-3 rounded-xl border prose-no-margin bg-fd-card text-fd-card-foreground shadow-md",
children: [/* @__PURE__ */ jsx("p", {
className: "font-semibold border-b pb-2",
children: "Example Requests"
}), children]
});
}
//#endregion
export { RequestTabs, getExampleRequests };
//# sourceMappingURL=request-tabs.js.map