OpenAPI: Generate document field on output MDX files

This commit is contained in:
fuma-nama
2024-08-13 00:10:30 +08:00
parent 52ef74062c
commit c7aa0909f0
20 changed files with 449 additions and 177 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@fumadocs/content-collections': patch
'fumadocs-mdx': patch
---
Improve Fumadocs OpenAPI support
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-openapi': minor
---
Generate `document` field on output MDX files
+1 -3
View File
@@ -46,9 +46,7 @@ export const blog = loader({
}),
});
export const openapi = createOpenAPI({
documentOrPath: './museum.yaml',
});
export const openapi = createOpenAPI({});
export type Page = InferPageType<typeof utils>;
export type Meta = InferMetaType<typeof utils>;
+1 -4
View File
@@ -3,7 +3,6 @@ import { createMDXSource, defaultSchemas } from 'fumadocs-mdx';
import { loader } from 'fumadocs-core/source';
import { attachFile, createOpenAPI } from 'fumadocs-openapi/server';
import { z } from 'zod';
import * as path from 'node:path';
const frontmatter = defaultSchemas.frontmatter.extend({
method: z.string().optional(),
@@ -22,6 +21,4 @@ export const { getPage, getPages, pageTree } = loader({
},
});
export const openapi = createOpenAPI({
documentOrPath: path.resolve('./unkey.json'),
});
export const openapi = createOpenAPI({});
@@ -133,6 +133,8 @@ export function createDocSchema(z: typeof Zod) {
description: z.string().optional(),
icon: z.string().optional(),
full: z.boolean().optional(),
// Fumadocs OpenAPI generated
_openapi: z.object({}).passthrough().optional(),
};
}
+3
View File
@@ -13,6 +13,9 @@ const frontmatterSchema = z.object({
description: z.string().optional(),
icon: z.string().optional(),
full: z.boolean().optional(),
// Fumadocs OpenAPI generated
_openapi: z.object({}).passthrough().optional(),
});
export const defaultSchemas = {
+33 -17
View File
@@ -43,31 +43,43 @@ export interface Config extends GenerateOptions {
* @defaultValue 'none'
*/
groupBy?: 'tag' | 'route' | 'none';
cwd?: string;
}
export async function generateFiles({
input,
output,
name: nameFn,
per = 'file',
cwd = process.cwd(),
groupBy = 'none',
...options
}: Config): Promise<void> {
export async function generateFiles(options: Config): Promise<void> {
const {
input,
output,
name: nameFn,
per = 'file',
groupBy = 'none',
cwd = process.cwd(),
} = options;
const outputDir = join(cwd, output);
const resolvedInputs = await fg.glob(input, { absolute: true, cwd });
const urlInputs: string[] = [];
const fileInputs: string[] = [];
for (const v of typeof input === 'string' ? [input] : input) {
if (isUrl(v)) {
urlInputs.push(v);
} else {
fileInputs.push(v);
}
}
const resolvedInputs = [
...(await fg.glob(fileInputs, { cwd, absolute: false })),
...urlInputs,
];
await Promise.all(
resolvedInputs.map(async (path) => {
resolvedInputs.map(async (pathOrUrl) => {
if (per === 'file') {
let filename = parse(path).name;
let filename = isUrl(pathOrUrl) ? 'index' : parse(pathOrUrl).name;
if (nameFn) filename = nameFn('file', filename);
const outPath = join(outputDir, `${filename}.mdx`);
const result = await generateAll(path, options);
const result = await generateAll(pathOrUrl, options);
await write(outPath, result);
console.log(`Generated: ${outPath}`);
return;
@@ -75,7 +87,7 @@ export async function generateFiles({
if (per === 'operation') {
const metaFiles = new Set<string>();
const results = await generateOperations(path, options);
const results = await generateOperations(pathOrUrl, options);
await Promise.all(
results.map(async (result) => {
@@ -132,7 +144,7 @@ export async function generateFiles({
return;
}
const results = await generateTags(path, options);
const results = await generateTags(pathOrUrl, options);
for (const result of results) {
let tagName = result.tag;
@@ -146,6 +158,10 @@ export async function generateFiles({
);
}
function isUrl(input: string): boolean {
return input.startsWith('https://') || input.startsWith('http://');
}
function getFilenameFromRoute(path: string): string {
return (
path
+66 -102
View File
@@ -1,12 +1,10 @@
import { resolve } from 'node:path';
import Parser from '@apidevtools/json-schema-ref-parser';
import { type OpenAPIV3 as OpenAPI } from 'openapi-types';
import { type TableOfContents } from 'fumadocs-core/server';
import { type StructuredData } from 'fumadocs-core/mdx-plugins';
import Slugger from 'github-slugger';
import { buildRoutes } from '@/build-routes';
import { generateDocument } from '@/utils/generate-document';
import { idToTitle } from '@/utils/id-to-title';
import { type ApiPageProps } from '@/server/api-page';
import { type Operation } from '@/server/api-page';
import type { MethodInformation, RouteInformation } from './types';
export type DocumentContext =
@@ -52,6 +50,8 @@ export interface GenerateOptions {
description: string | undefined,
context: DocumentContext,
) => Record<string, unknown>;
cwd?: string;
}
export interface GenerateTagOutput {
@@ -66,13 +66,27 @@ export interface GenerateOperationOutput {
route: RouteInformation;
}
async function dereference(
pathOrDocument: string | OpenAPI.Document,
options: GenerateOptions,
): Promise<OpenAPI.Document> {
return await Parser.dereference<OpenAPI.Document>(
// resolve paths
typeof pathOrDocument === 'string' &&
!pathOrDocument.startsWith('http://') &&
!pathOrDocument.startsWith('https://')
? resolve(options.cwd ?? process.cwd(), pathOrDocument)
: pathOrDocument,
);
}
export async function generateAll(
pathOrDocument: string | OpenAPI.Document,
options: GenerateOptions = {},
): Promise<string> {
const document = await Parser.dereference<OpenAPI.Document>(pathOrDocument);
const document = await dereference(pathOrDocument, options);
const routes = buildRoutes(document).get('all') ?? [];
const operations: { path: string; method: OpenAPI.HttpMethods }[] = [];
const operations: Operation[] = [];
for (const route of routes) {
for (const method of route.methods) {
@@ -83,25 +97,29 @@ export async function generateAll(
}
}
return generateDocument(
pageContent(document, { operations, hasHead: true }),
options,
{
title: document.info.title,
description: document.info.description,
context: {
type: 'file',
routes,
},
return generateDocument({
...options,
dereferenced: document,
title: document.info.title,
description: document.info.description,
page: {
operations,
hasHead: true,
document: pathOrDocument,
},
);
context: {
type: 'file',
routes,
},
});
}
export async function generateOperations(
pathOrDocument: string | OpenAPI.Document,
options: GenerateOptions = {},
): Promise<GenerateOperationOutput[]> {
const document = await Parser.dereference<OpenAPI.Document>(pathOrDocument);
const document = await dereference(pathOrDocument, options);
const routes = buildRoutes(document).get('all') ?? [];
return routes.flatMap<GenerateOperationOutput>((route) => {
@@ -109,8 +127,9 @@ export async function generateOperations(
if (!method.operationId)
throw new Error('Operation ID is required for generating docs.');
const content = generateDocument(
pageContent(document, {
const content = generateDocument({
...options,
page: {
operations: [
{
path: route.path,
@@ -118,18 +137,17 @@ export async function generateOperations(
},
],
hasHead: false,
}),
options,
{
title: method.summary ?? idToTitle(method.operationId),
description: method.description,
context: {
type: 'operation',
endpoint: method,
route,
},
document: pathOrDocument,
},
);
dereferenced: document,
title: method.summary ?? idToTitle(method.operationId),
description: method.description,
context: {
type: 'operation',
endpoint: method,
route,
},
});
return {
content,
@@ -144,14 +162,14 @@ export async function generateTags(
pathOrDocument: string | OpenAPI.Document,
options: GenerateOptions = {},
): Promise<GenerateTagOutput[]> {
const document = await Parser.dereference<OpenAPI.Document>(pathOrDocument);
const document = await dereference(pathOrDocument, options);
const tags = Array.from(buildRoutes(document).entries());
return tags
.filter(([tag]) => tag !== 'all')
.map(([tag, routes]) => {
const info = document.tags?.find((t) => t.name === tag);
const operations: { path: string; method: OpenAPI.HttpMethods }[] = [];
const operations: Operation[] = [];
for (const route of routes) {
for (const method of route.methods) {
@@ -164,76 +182,22 @@ export async function generateTags(
return {
tag,
content: generateDocument(
pageContent(document, { operations, hasHead: true }),
options,
{
title: idToTitle(tag),
description: info?.description,
context: {
type: 'tag',
tag: info,
routes,
},
content: generateDocument({
...options,
page: {
document: pathOrDocument,
operations,
hasHead: true,
},
),
dereferenced: document,
title: idToTitle(tag),
description: info?.description,
context: {
type: 'tag',
tag: info,
routes,
},
}),
} satisfies GenerateTagOutput;
});
}
function pageContent(
doc: OpenAPI.Document,
props: Omit<ApiPageProps, 'document'>,
): string {
const slugger = new Slugger();
const toc: TableOfContents = [];
const structuredData: StructuredData = { headings: [], contents: [] };
for (const item of props.operations) {
const operation = doc.paths[item.path]?.[item.method];
if (!operation) continue;
if (props.hasHead && operation.operationId) {
const title =
operation.summary ??
(operation.operationId ? idToTitle(operation.operationId) : item.path);
const id = slugger.slug(title);
toc.push({
depth: 2,
title,
url: `#${id}`,
});
structuredData.headings.push({
content: title,
id,
});
}
if (operation.description)
structuredData.contents.push({
content: operation.description,
heading: structuredData.headings.at(-1)?.id,
});
}
// modify toc and structured data if possible
// it may not be compatible with other content sources except Fumadocs MDX
// TODO: Maybe add to frontmatter and let developers to handle them?
return `<APIPage operations={${JSON.stringify(props.operations)}} hasHead={${JSON.stringify(props.hasHead)}} />
export function startup() {
if (typeof toc !== 'undefined') {
// toc might be immutable
while (toc.length > 0) toc.pop()
toc.push(...${JSON.stringify(toc)})
}
if (typeof structuredData !== 'undefined') {
structuredData.headings = ${JSON.stringify(structuredData.headings)}
structuredData.contents = ${JSON.stringify(structuredData.contents)}
}
}
{startup()}`;
}
+23 -5
View File
@@ -1,6 +1,7 @@
import type { ReactElement } from 'react';
import { type OpenAPIV3 as OpenAPI } from 'openapi-types';
import Slugger from 'github-slugger';
import Parser from '@apidevtools/json-schema-ref-parser';
import { Operation } from '@/render/operation';
import type { RenderContext } from '@/types';
import { createMethod } from '@/schema/method';
@@ -11,18 +12,35 @@ export interface ApiPageProps
RenderContext,
'generateCodeSamples' | 'generateTypeScriptSchema'
> {
document: OpenAPI.Document;
document: string | OpenAPI.Document;
/**
* An array of operation
* An array of operations
*/
operations: { path: string; method: OpenAPI.HttpMethods }[];
operations: Operation[];
hasHead: boolean;
renderer?: Partial<Renderer>;
}
export function APIPage(props: ApiPageProps): ReactElement {
const { operations, document, hasHead = true } = props;
const cache = new Map<string, OpenAPI.Document>();
export interface Operation {
path: string;
method: OpenAPI.HttpMethods;
}
export async function APIPage(props: ApiPageProps): Promise<ReactElement> {
const { operations, hasHead = true } = props;
let document: OpenAPI.Document;
if (typeof props.document === 'string') {
const cached = cache.get(props.document);
document =
cached ?? (await Parser.dereference<OpenAPI.Document>(props.document));
cache.set(props.document, document);
} else {
document = await Parser.dereference<OpenAPI.Document>(props.document);
}
const ctx = getContext(document, props);
return (
+7 -9
View File
@@ -1,25 +1,23 @@
import type { OpenAPIV3 as OpenAPI } from 'openapi-types';
import Parser from '@apidevtools/json-schema-ref-parser';
import { type FC } from 'react';
import { APIPage, type ApiPageProps } from '@/server/api-page';
export interface OpenAPIOptions
extends Omit<Partial<ApiPageProps>, 'document'> {
documentOrPath: string | OpenAPI.Document;
/**
* @deprecated Pass document to `APIPage` instead
*/
documentOrPath?: string | OpenAPI.Document;
}
export interface OpenAPIServer {
APIPage: FC<Omit<ApiPageProps, 'document'>>;
APIPage: FC<ApiPageProps>;
}
export function createOpenAPI(options: OpenAPIOptions): OpenAPIServer {
const document = Parser.dereference(options.documentOrPath);
return {
APIPage: async (props) => {
return (
<APIPage document={(await document) as OpenAPI.Document} {...props} />
);
APIPage(props) {
return <APIPage {...props} />;
},
};
}
+14 -3
View File
@@ -10,9 +10,20 @@ import { getBadgeColor } from '@/ui/components/variants';
export const attachFile: BuildPageTreeOptions['attachFile'] = (node, file) => {
if (!file) return node;
const data = file.data.data as object;
let method: string | undefined;
if ('method' in data && typeof data.method === 'string') {
const color = getBadgeColor(data.method);
if ('_openapi' in data && typeof data._openapi === 'object') {
const meta = data._openapi as {
method?: string;
};
method = meta.method;
} else if ('method' in data && typeof data.method === 'string') {
method = data.method;
}
if (method) {
const color = getBadgeColor(method);
node.name = (
<>
@@ -20,7 +31,7 @@ export const attachFile: BuildPageTreeOptions['attachFile'] = (node, file) => {
<span
className={badgeVariants({ className: 'ms-auto text-nowrap', color })}
>
{data.method}
{method}
</span>
</>
);
+102 -17
View File
@@ -1,31 +1,55 @@
import { dump } from 'js-yaml';
import { type OpenAPIV3 as OpenAPI } from 'openapi-types';
import Slugger from 'github-slugger';
import { type TableOfContents } from 'fumadocs-core/server';
import { type StructuredData } from 'fumadocs-core/mdx-plugins';
import { type ApiPageProps } from '@/server/api-page';
import type { DocumentContext, GenerateOptions } from '@/generate';
import { idToTitle } from '@/utils/id-to-title';
interface StaticData {
toc: TableOfContents;
structuredData: StructuredData;
}
export function generateDocument(
content: string,
options: GenerateOptions,
frontmatter: {
options: GenerateOptions & {
dereferenced: OpenAPI.Document;
page: ApiPageProps;
title: string;
description?: string;
context: DocumentContext;
},
): string {
const { frontmatter } = options;
const out: string[] = [];
const extend = frontmatter?.(
options.title,
options.description,
options.context,
);
let meta: object | undefined;
if (options.context.type === 'operation') {
meta = {
method: options.context.endpoint.method,
route: options.context.route.path,
};
}
const data = generateStaticData(options.dereferenced, options.page);
const banner = dump({
title: frontmatter.title,
description: frontmatter.description,
title: options.title,
description: options.description,
full: true,
...(frontmatter.context.type === 'operation'
? {
method: frontmatter.context.endpoint.method,
route: frontmatter.context.route.path,
}
: undefined),
...options.frontmatter?.(
frontmatter.title,
frontmatter.description,
frontmatter.context,
),
...extend,
_openapi: {
...meta,
...data,
...(extend?._openapi as object | undefined),
},
}).trim();
if (banner.length > 0) out.push(`---\n${banner}\n---`);
@@ -40,7 +64,68 @@ export function generateDocument(
out.push(imports);
}
out.push(content);
out.push(pageContent(data, options.page));
return out.join('\n\n');
}
function generateStaticData(
dereferenced: OpenAPI.Document,
props: ApiPageProps,
): StaticData {
const slugger = new Slugger();
const toc: TableOfContents = [];
const structuredData: StructuredData = { headings: [], contents: [] };
for (const item of props.operations) {
const operation = dereferenced.paths[item.path]?.[item.method];
if (!operation) continue;
if (props.hasHead && operation.operationId) {
const title =
operation.summary ??
(operation.operationId ? idToTitle(operation.operationId) : item.path);
const id = slugger.slug(title);
toc.push({
depth: 2,
title,
url: `#${id}`,
});
structuredData.headings.push({
content: title,
id,
});
}
if (operation.description)
structuredData.contents.push({
content: operation.description,
heading: structuredData.headings.at(-1)?.id,
});
}
return { toc, structuredData };
}
function pageContent(data: StaticData, props: ApiPageProps): string {
// modify toc and structured data if possible
// it may not be compatible with other content sources except Fumadocs MDX
// TODO: Maybe add to frontmatter and let developers to handle them?
return `<APIPage document={${JSON.stringify(props.document)}} operations={${JSON.stringify(props.operations)}} hasHead={${JSON.stringify(props.hasHead)}} />
export function startup() {
if (typeof toc !== 'undefined') {
// toc might be immutable
while (toc.length > 0) toc.pop()
toc.push(...${JSON.stringify(data.toc)})
}
if (typeof structuredData !== 'undefined') {
structuredData.headings = ${JSON.stringify(data.structuredData.headings)}
structuredData.contents = ${JSON.stringify(data.structuredData.contents)}
}
}
{startup()}`;
}
+9 -10
View File
@@ -13,22 +13,24 @@ describe('Utilities', () => {
});
describe('Generate documents', () => {
const cwd = fileURLToPath(new URL('./', import.meta.url));
afterEach(() => {
vi.restoreAllMocks();
});
test('Pet Store', async () => {
const result = await generateAll(
fileURLToPath(new URL('./fixtures/petstore.yaml', import.meta.url)),
);
const result = await generateAll('./fixtures/petstore.yaml', {
cwd,
});
await expect(result).toMatchFileSnapshot('./out/petstore.mdx');
});
test('Museum', async () => {
const tags = await generateTags(
fileURLToPath(new URL('./fixtures/museum.yaml', import.meta.url)),
);
const tags = await generateTags('./fixtures/museum.yaml', {
cwd,
});
for (const tag of tags) {
await expect(tag.content).toMatchFileSnapshot(
@@ -38,9 +40,7 @@ describe('Generate documents', () => {
});
test('Unkey', async () => {
const tags = await generateTags(
fileURLToPath(new URL('./fixtures/unkey.json', import.meta.url)),
);
const tags = await generateTags('./fixtures/unkey.json', { cwd });
for (const tag of tags) {
await expect(tag.content).toMatchFileSnapshot(
@@ -50,7 +50,6 @@ describe('Generate documents', () => {
});
test('Generate Files', async () => {
const cwd = fileURLToPath(new URL('./', import.meta.url));
vi.mock('node:fs/promises', async (importOriginal) => {
return {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- mock
+43 -1
View File
@@ -2,9 +2,51 @@
title: Events
description: Special events hosted by the Museum
full: true
_openapi:
toc:
- depth: 2
title: List special events
url: '#list-special-events'
- depth: 2
title: Create special events
url: '#create-special-events'
- depth: 2
title: Get special event
url: '#get-special-event'
- depth: 2
title: Update special event
url: '#update-special-event'
- depth: 2
title: Delete special event
url: '#delete-special-event'
structuredData:
headings:
- content: List special events
id: list-special-events
- content: Create special events
id: create-special-events
- content: Get special event
id: get-special-event
- content: Update special event
id: update-special-event
- content: Delete special event
id: delete-special-event
contents:
- content: Return a list of upcoming special events at the museum.
heading: list-special-events
- content: Creates a new special event for the museum.
heading: create-special-events
- content: Get details about a special event.
heading: get-special-event
- content: Update the details of a special event
heading: update-special-event
- content: >-
Delete a special event from the collection. Allows museum to cancel
planned events.
heading: delete-special-event
---
<APIPage operations={[{"method":"get","path":"/special-events"},{"method":"post","path":"/special-events"},{"method":"get","path":"/special-events/{eventId}"},{"method":"patch","path":"/special-events/{eventId}"},{"method":"delete","path":"/special-events/{eventId}"}]} hasHead={true} />
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/special-events"},{"method":"post","path":"/special-events"},{"method":"get","path":"/special-events/{eventId}"},{"method":"patch","path":"/special-events/{eventId}"},{"method":"delete","path":"/special-events/{eventId}"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
@@ -2,9 +2,21 @@
title: Operations
description: Operational information about the museum.
full: true
_openapi:
toc:
- depth: 2
title: Get museum hours
url: '#get-museum-hours'
structuredData:
headings:
- content: Get museum hours
id: get-museum-hours
contents:
- content: Get upcoming museum operating hours
heading: get-museum-hours
---
<APIPage operations={[{"method":"get","path":"/museum-hours"}]} hasHead={true} />
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"get","path":"/museum-hours"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
+22 -1
View File
@@ -2,9 +2,30 @@
title: Tickets
description: Museum tickets for general entrance or special events.
full: true
_openapi:
toc:
- depth: 2
title: Buy museum tickets
url: '#buy-museum-tickets'
- depth: 2
title: Get ticket QR code
url: '#get-ticket-qr-code'
structuredData:
headings:
- content: Buy museum tickets
id: buy-museum-tickets
- content: Get ticket QR code
id: get-ticket-qr-code
contents:
- content: Purchase museum tickets for general entry or special events.
heading: buy-museum-tickets
- content: >-
Return an image of your ticket with scannable QR code. Used for event
entry.
heading: get-ticket-qr-code
---
<APIPage operations={[{"method":"post","path":"/tickets"},{"method":"get","path":"/tickets/{ticketId}/qr"}]} hasHead={true} />
<APIPage document={"./fixtures/museum.yaml"} operations={[{"method":"post","path":"/tickets"},{"method":"get","path":"/tickets/{ticketId}/qr"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
+21 -1
View File
@@ -1,9 +1,29 @@
---
title: Swagger Petstore
full: true
_openapi:
toc:
- depth: 2
title: List all pets
url: '#list-all-pets'
- depth: 2
title: Create a pet
url: '#create-a-pet'
- depth: 2
title: Info for a specific pet
url: '#info-for-a-specific-pet'
structuredData:
headings:
- content: List all pets
id: list-all-pets
- content: Create a pet
id: create-a-pet
- content: Info for a specific pet
id: info-for-a-specific-pet
contents: []
---
<APIPage operations={[{"method":"get","path":"/pets"},{"method":"post","path":"/pets"},{"method":"get","path":"/pets/{petId}"}]} hasHead={true} />
<APIPage document={"./fixtures/petstore.yaml"} operations={[{"method":"get","path":"/pets"},{"method":"post","path":"/pets"},{"method":"get","path":"/pets/{petId}"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
+26 -1
View File
@@ -1,9 +1,34 @@
---
title: Apis
full: true
_openapi:
toc:
- depth: 2
title: Get Api
url: '#get-api'
- depth: 2
title: Create Api
url: '#create-api'
- depth: 2
title: List Keys
url: '#list-keys'
- depth: 2
title: Delete Api
url: '#delete-api'
structuredData:
headings:
- content: Get Api
id: get-api
- content: Create Api
id: create-api
- content: List Keys
id: list-keys
- content: Delete Api
id: delete-api
contents: []
---
<APIPage operations={[{"method":"get","path":"/v1/apis.getApi"},{"method":"post","path":"/v1/apis.createApi"},{"method":"get","path":"/v1/apis.listKeys"},{"method":"post","path":"/v1/apis.deleteApi"}]} hasHead={true} />
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/apis.getApi"},{"method":"post","path":"/v1/apis.createApi"},{"method":"get","path":"/v1/apis.listKeys"},{"method":"post","path":"/v1/apis.deleteApi"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
+41 -1
View File
@@ -1,9 +1,49 @@
---
title: Keys
full: true
_openapi:
toc:
- depth: 2
title: Get Key
url: '#get-key'
- depth: 2
title: Delete Key
url: '#delete-key'
- depth: 2
title: Create Key
url: '#create-key'
- depth: 2
title: Verify Key
url: '#verify-key'
- depth: 2
title: Update Key
url: '#update-key'
- depth: 2
title: Update Remaining
url: '#update-remaining'
- depth: 2
title: Get Verifications
url: '#get-verifications'
structuredData:
headings:
- content: Get Key
id: get-key
- content: Delete Key
id: delete-key
- content: Create Key
id: create-key
- content: Verify Key
id: verify-key
- content: Update Key
id: update-key
- content: Update Remaining
id: update-remaining
- content: Get Verifications
id: get-verifications
contents: []
---
<APIPage operations={[{"method":"get","path":"/v1/keys.getKey"},{"method":"post","path":"/v1/keys.deleteKey"},{"method":"post","path":"/v1/keys.createKey"},{"method":"post","path":"/v1/keys.verifyKey"},{"method":"post","path":"/v1/keys.updateKey"},{"method":"post","path":"/v1/keys.updateRemaining"},{"method":"get","path":"/v1/keys.getVerifications"}]} hasHead={true} />
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/keys.getKey"},{"method":"post","path":"/v1/keys.deleteKey"},{"method":"post","path":"/v1/keys.createKey"},{"method":"post","path":"/v1/keys.verifyKey"},{"method":"post","path":"/v1/keys.updateKey"},{"method":"post","path":"/v1/keys.updateRemaining"},{"method":"get","path":"/v1/keys.getVerifications"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {
+11 -1
View File
@@ -1,9 +1,19 @@
---
title: Liveness
full: true
_openapi:
toc:
- depth: 2
title: Check Liveness
url: '#check-liveness'
structuredData:
headings:
- content: Check Liveness
id: check-liveness
contents: []
---
<APIPage operations={[{"method":"get","path":"/v1/liveness"}]} hasHead={true} />
<APIPage document={"./fixtures/unkey.json"} operations={[{"method":"get","path":"/v1/liveness"}]} hasHead={true} />
export function startup() {
if (typeof toc !== 'undefined') {