mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Translate OpenAPI blocks (#3166)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gitbook/react-openapi": patch
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Translate OpenAPI blocks
|
||||
@@ -10,18 +10,18 @@ const testCases: TestsCase[] = [
|
||||
{ name: 'OpenAPI', url: '/snyk-api/reference/apps', run: waitForCookiesDialog },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Nexthink',
|
||||
contentBaseURL: 'https://docs.nexthink.com',
|
||||
tests: [
|
||||
{
|
||||
name: 'Home',
|
||||
url: '/',
|
||||
screenshot: { waitForTOCScrolling: false },
|
||||
run: waitForCookiesDialog,
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// name: 'Nexthink',
|
||||
// contentBaseURL: 'https://docs.nexthink.com',
|
||||
// tests: [
|
||||
// {
|
||||
// name: 'Home',
|
||||
// url: '/',
|
||||
// screenshot: { waitForTOCScrolling: false },
|
||||
// run: waitForCookiesDialog,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
name: 'asiksupport-stg.dto.kemkes.go.id',
|
||||
contentBaseURL: 'https://asiksupport-stg.dto.kemkes.go.id',
|
||||
@@ -157,11 +157,11 @@ const testCases: TestsCase[] = [
|
||||
contentBaseURL: 'https://wiki.redmodding.org',
|
||||
tests: [{ name: 'Home', url: '/' }],
|
||||
},
|
||||
{
|
||||
name: 'docs.cherry-ai.com',
|
||||
contentBaseURL: 'https://docs.cherry-ai.com',
|
||||
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
},
|
||||
// {
|
||||
// name: 'docs.cherry-ai.com',
|
||||
// contentBaseURL: 'https://docs.cherry-ai.com',
|
||||
// tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
|
||||
// },
|
||||
{
|
||||
name: 'docs.snyk.io',
|
||||
contentBaseURL: 'https://docs.snyk.io',
|
||||
|
||||
@@ -51,7 +51,7 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
|
||||
return (
|
||||
<BaseOpenAPIOperation
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl })}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
|
||||
<BaseOpenAPISchemas
|
||||
schemas={data.schemas}
|
||||
grouped={block.data.grouped}
|
||||
context={getOpenAPIContext({ props, specUrl })}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
|
||||
return (
|
||||
<BaseOpenAPIWebhook
|
||||
data={data}
|
||||
context={getOpenAPIContext({ props, specUrl })}
|
||||
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
|
||||
className="openapi-block"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { JSONDocument } from '@gitbook/api';
|
||||
import { Icon } from '@gitbook/icons';
|
||||
import type { OpenAPIContext } from '@gitbook/react-openapi';
|
||||
import { type OpenAPIContextInput, checkIsValidLocale } from '@gitbook/react-openapi';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
@@ -11,11 +11,13 @@ import { Heading } from '../Heading';
|
||||
|
||||
import './scalar.css';
|
||||
import './style.css';
|
||||
import { DEFAULT_LOCALE, getCustomizationLocale } from '@/intl/server';
|
||||
import type {
|
||||
AnyOpenAPIOperationsBlock,
|
||||
OpenAPISchemasBlock,
|
||||
OpenAPIWebhookBlock,
|
||||
} from '@/lib/openapi/types';
|
||||
import type { GitBookAnyContext } from '@v2/lib/context';
|
||||
|
||||
/**
|
||||
* Get the OpenAPI context to render a block.
|
||||
@@ -23,9 +25,17 @@ import type {
|
||||
export function getOpenAPIContext(args: {
|
||||
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
|
||||
specUrl: string;
|
||||
}): OpenAPIContext {
|
||||
const { props, specUrl } = args;
|
||||
context: GitBookAnyContext | undefined;
|
||||
}): OpenAPIContextInput {
|
||||
const { props, specUrl, context } = args;
|
||||
const { block } = props;
|
||||
|
||||
const customization = context && 'customization' in context ? context.customization : null;
|
||||
const customizationLocale = customization
|
||||
? getCustomizationLocale(customization)
|
||||
: DEFAULT_LOCALE;
|
||||
const locale = checkIsValidLocale(customizationLocale) ? customizationLocale : DEFAULT_LOCALE;
|
||||
|
||||
return {
|
||||
specUrl,
|
||||
icons: {
|
||||
@@ -73,5 +83,6 @@ export function getOpenAPIContext(args: {
|
||||
defaultInteractiveOpened: props.context.mode === 'print',
|
||||
id: block.meta?.id,
|
||||
blockKey: block.key,
|
||||
locale,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -229,19 +229,19 @@
|
||||
}
|
||||
|
||||
.openapi-schema-required {
|
||||
@apply text-warning-subtle text-[0.813rem];
|
||||
@apply text-warning-subtle text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-optional {
|
||||
@apply text-info-subtle text-[0.813rem];
|
||||
@apply text-info-subtle text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-readonly {
|
||||
@apply text-primary-subtle/9 text-[0.813rem];
|
||||
@apply text-primary-subtle/9 text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-writeonly {
|
||||
@apply text-success dark:text-success-subtle/9 text-[0.813rem];
|
||||
@apply text-success dark:text-success-subtle/9 text-[0.813rem] lowercase;
|
||||
}
|
||||
|
||||
.openapi-schema-type {
|
||||
|
||||
@@ -4,17 +4,26 @@ import { type TranslationLanguage, languages } from './translations';
|
||||
|
||||
export * from './translate';
|
||||
|
||||
export const DEFAULT_LOCALE = 'en';
|
||||
|
||||
/**
|
||||
* Get the locale of the customization.
|
||||
*/
|
||||
export function getCustomizationLocale(customization: SiteCustomizationSettings): string {
|
||||
return customization.internationalization.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the translation context for a space to use in the server components.
|
||||
*/
|
||||
export function getSpaceLanguage(customization: SiteCustomizationSettings): TranslationLanguage {
|
||||
const fallback = languages.en;
|
||||
const fallback = languages[DEFAULT_LOCALE];
|
||||
|
||||
const { locale } = customization.internationalization;
|
||||
const locale = getCustomizationLocale(customization);
|
||||
|
||||
let language = fallback;
|
||||
// @ts-ignore
|
||||
if (locale !== 'en' && languages[locale]) {
|
||||
if (locale !== DEFAULT_LOCALE && languages[locale]) {
|
||||
// @ts-ignore
|
||||
language = languages[locale];
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ export function InteractiveSection(props: {
|
||||
) : null}
|
||||
{header}
|
||||
</SectionHeaderContent>
|
||||
{/* biome-ignore lint/a11y/useKeyWithClickEvents: we prevent default here */}
|
||||
<div
|
||||
className={clsx(
|
||||
'openapi-section-header-controls',
|
||||
|
||||
@@ -6,9 +6,10 @@ import {
|
||||
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
|
||||
import { ScalarApiButton } from './ScalarApiButton';
|
||||
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
|
||||
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
||||
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
import { getDefaultServerURL } from './util/server';
|
||||
import { checkIsReference } from './utils';
|
||||
|
||||
@@ -44,7 +45,12 @@ export function OpenAPICodeSample(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<OpenAPICodeSampleBody data={data} items={samples} selectIcon={context.icons.chevronDown} />
|
||||
<OpenAPICodeSampleBody
|
||||
context={getOpenAPIClientContext(context)}
|
||||
data={data}
|
||||
items={samples}
|
||||
selectIcon={context.icons.chevronDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -215,7 +221,14 @@ function OpenAPICodeSampleFooter(props: {
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
{!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl} />}
|
||||
{!hideTryItPanel && (
|
||||
<ScalarApiButton
|
||||
context={getOpenAPIClientContext(context)}
|
||||
method={method}
|
||||
path={path}
|
||||
specUrl={specUrl}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useStore } from 'zustand';
|
||||
import { OpenAPIPath } from './OpenAPIPath';
|
||||
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
||||
import { StaticSection } from './StaticSection';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
|
||||
@@ -26,12 +27,13 @@ function OpenAPICodeSampleHeader(props: {
|
||||
items: CodeSampleItem[];
|
||||
data: OpenAPIOperationData;
|
||||
selectIcon?: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { data, items, selectIcon } = props;
|
||||
const { data, items, selectIcon, context } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<OpenAPIPath canCopy={false} withServer={false} data={data} />
|
||||
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
|
||||
{items.length > 1 ? (
|
||||
<OpenAPISelect
|
||||
icon={selectIcon}
|
||||
@@ -56,8 +58,9 @@ export function OpenAPICodeSampleBody(props: {
|
||||
items: CodeSampleItem[];
|
||||
data: OpenAPIOperationData;
|
||||
selectIcon?: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { items, data, selectIcon } = props;
|
||||
const { items, data, selectIcon, context } = props;
|
||||
if (!items[0]) {
|
||||
throw new Error('No items provided');
|
||||
}
|
||||
@@ -72,7 +75,14 @@ export function OpenAPICodeSampleBody(props: {
|
||||
|
||||
return (
|
||||
<StaticSection
|
||||
header={<OpenAPICodeSampleHeader selectIcon={selectIcon} data={data} items={items} />}
|
||||
header={
|
||||
<OpenAPICodeSampleHeader
|
||||
context={context}
|
||||
selectIcon={selectIcon}
|
||||
data={data}
|
||||
items={items}
|
||||
/>
|
||||
}
|
||||
className="openapi-codesample"
|
||||
>
|
||||
<div id={selected.key as string} className="openapi-codesample-panel">
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
|
||||
export function OpenAPICopyButton(
|
||||
props: ButtonProps & {
|
||||
value: string;
|
||||
children: React.ReactNode;
|
||||
context: OpenAPIClientContext;
|
||||
label?: string;
|
||||
/**
|
||||
* Whether to show a tooltip.
|
||||
@@ -15,7 +18,7 @@ export function OpenAPICopyButton(
|
||||
withTooltip?: boolean;
|
||||
}
|
||||
) {
|
||||
const { value, label, children, onPress, className, withTooltip = true } = props;
|
||||
const { value, label, children, onPress, className, context, withTooltip = true } = props;
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -60,7 +63,9 @@ export function OpenAPICopyButton(
|
||||
offset={4}
|
||||
className="openapi-tooltip"
|
||||
>
|
||||
{copied ? 'Copied' : label || 'Copy to clipboard'}
|
||||
{copied
|
||||
? t(context.translation, 'copied')
|
||||
: label || t(context.translation, 'copy_to_clipboard')}
|
||||
</Tooltip>
|
||||
</TooltipTrigger>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
|
||||
import { json2xml } from './json2xml';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import type { OpenAPIContext } from './types';
|
||||
import { t } from './translate';
|
||||
|
||||
/**
|
||||
* Display an example.
|
||||
@@ -15,7 +16,7 @@ export function OpenAPIExample(props: {
|
||||
const code = stringifyExample({ example, xml: syntax === 'xml' });
|
||||
|
||||
if (code === null) {
|
||||
return <OpenAPIEmptyExample />;
|
||||
return <OpenAPIEmptyExample context={context} />;
|
||||
}
|
||||
|
||||
return context.renderCodeBlock({ code, syntax });
|
||||
@@ -42,10 +43,13 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
|
||||
/**
|
||||
* Empty response example.
|
||||
*/
|
||||
export function OpenAPIEmptyExample() {
|
||||
export function OpenAPIEmptyExample(props: {
|
||||
context: OpenAPIUniversalContext;
|
||||
}) {
|
||||
const { context } = props;
|
||||
return (
|
||||
<pre className="openapi-example-empty">
|
||||
<p>No Content</p>
|
||||
<p>{t(context.translation, 'no_content')}</p>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import type { Key } from 'react-aria';
|
||||
import { OpenAPIEmptyExample } from './OpenAPIExample';
|
||||
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
||||
import { StaticSection } from './StaticSection';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
|
||||
type OpenAPIMediaTypeItem = OpenAPISelectItem & {
|
||||
body: React.ReactNode;
|
||||
@@ -24,8 +26,9 @@ export function OpenAPIMediaTypeContent(props: {
|
||||
items: OpenAPIMediaTypeItem[];
|
||||
selectIcon?: React.ReactNode;
|
||||
stateKey: string;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { stateKey, items, selectIcon } = props;
|
||||
const { stateKey, items, selectIcon, context } = props;
|
||||
const state = useMediaTypesState(stateKey, items[0]?.key);
|
||||
|
||||
const examples = items.find((item) => item.key === state.key)?.examples ?? [];
|
||||
@@ -48,7 +51,12 @@ export function OpenAPIMediaTypeContent(props: {
|
||||
}
|
||||
className="openapi-response-media-types-examples"
|
||||
>
|
||||
<OpenAPIMediaTypeBody stateKey={stateKey} items={items} examples={examples} />
|
||||
<OpenAPIMediaTypeBody
|
||||
context={context}
|
||||
stateKey={stateKey}
|
||||
items={items}
|
||||
examples={examples}
|
||||
/>
|
||||
</StaticSection>
|
||||
);
|
||||
}
|
||||
@@ -100,8 +108,9 @@ function OpenAPIMediaTypeBody(props: {
|
||||
items: OpenAPIMediaTypeItem[];
|
||||
examples?: OpenAPIMediaTypeItem[];
|
||||
stateKey: string;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { stateKey, items, examples } = props;
|
||||
const { stateKey, items, examples, context } = props;
|
||||
const state = useMediaTypesState(stateKey, items[0]?.key);
|
||||
|
||||
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
|
||||
@@ -120,7 +129,7 @@ function OpenAPIMediaTypeBody(props: {
|
||||
examples.find((example) => example.key === exampleState.key) ?? examples[0];
|
||||
|
||||
if (!selectedExample) {
|
||||
return <OpenAPIEmptyExample />;
|
||||
return <OpenAPIEmptyExample context={context} />;
|
||||
}
|
||||
|
||||
return selectedExample.body;
|
||||
|
||||
@@ -3,7 +3,8 @@ import { OpenAPICodeSample } from './OpenAPICodeSample';
|
||||
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
|
||||
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
|
||||
import { OpenAPISummary } from './common/OpenAPISummary';
|
||||
import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
||||
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
|
||||
/**
|
||||
* Display an interactive OpenAPI operation.
|
||||
@@ -11,9 +12,11 @@ import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
||||
export function OpenAPIOperation(props: {
|
||||
className?: string;
|
||||
data: OpenAPIOperationData;
|
||||
context: OpenAPIContext;
|
||||
context: OpenAPIContextInput;
|
||||
}) {
|
||||
const { className, data, context } = props;
|
||||
const { className, data, context: contextInput } = props;
|
||||
|
||||
const context = resolveOpenAPIContext(contextInput);
|
||||
|
||||
return (
|
||||
<div className={clsx('openapi-operation', className)}>
|
||||
@@ -22,8 +25,8 @@ export function OpenAPIOperation(props: {
|
||||
<OpenAPIColumnSpec data={data} context={context} />
|
||||
<div className="openapi-column-preview">
|
||||
<div className="openapi-column-preview-body">
|
||||
<OpenAPICodeSample {...props} />
|
||||
<OpenAPIResponseExample {...props} />
|
||||
<OpenAPICodeSample data={data} context={context} />
|
||||
<OpenAPIResponseExample data={data} context={context} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { Markdown } from './Markdown';
|
||||
import type { OpenAPIContext } from './context';
|
||||
import { resolveDescription } from './utils';
|
||||
|
||||
/**
|
||||
* Display the description of an OpenAPI operation.
|
||||
*/
|
||||
export function OpenAPIOperationDescription(props: {
|
||||
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
||||
context: OpenAPIContext;
|
||||
}) {
|
||||
const { operation } = props;
|
||||
if (operation['x-gitbook-description-document']) {
|
||||
return (
|
||||
<div className="openapi-intro">
|
||||
{props.context.renderDocument({
|
||||
document: operation['x-gitbook-description-document'],
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const description = resolveDescription(operation);
|
||||
if (!description) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="openapi-intro">
|
||||
<Markdown className="openapi-description" source={description} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { OpenAPIStability } from '@gitbook/openapi-parser';
|
||||
import type { OpenAPIContext } from './context';
|
||||
import { t } from './translate';
|
||||
|
||||
/**
|
||||
* Display the stability of an OpenAPI operation.
|
||||
*/
|
||||
export function OpenAPIOperationStability(props: {
|
||||
stability: OpenAPIStability;
|
||||
context: OpenAPIContext;
|
||||
}) {
|
||||
const { stability, context } = props;
|
||||
|
||||
const stabilityLabel = getStabilityLabel(stability, context);
|
||||
|
||||
if (!stabilityLabel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`openapi-stability openapi-stability-${stability}`}>{stabilityLabel}</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stability label for the given stability level.
|
||||
*/
|
||||
function getStabilityLabel(stability: OpenAPIStability, context: OpenAPIContext) {
|
||||
switch (stability) {
|
||||
case 'experimental':
|
||||
return t(context.translation, 'stability_experimental');
|
||||
case 'alpha':
|
||||
return t(context.translation, 'stability_alpha');
|
||||
case 'beta':
|
||||
return t(context.translation, 'stability_beta');
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { OpenAPICopyButton } from './OpenAPICopyButton';
|
||||
import { type OpenAPIUniversalContext, getOpenAPIClientContext } from './context';
|
||||
import type { OpenAPIOperationData } from './types';
|
||||
import { getDefaultServerURL } from './util/server';
|
||||
|
||||
@@ -7,6 +8,7 @@ import { getDefaultServerURL } from './util/server';
|
||||
*/
|
||||
export function OpenAPIPath(props: {
|
||||
data: OpenAPIOperationData;
|
||||
context: OpenAPIUniversalContext;
|
||||
/** Whether to show the server URL.
|
||||
* @default true
|
||||
*/
|
||||
@@ -17,7 +19,7 @@ export function OpenAPIPath(props: {
|
||||
*/
|
||||
canCopy?: boolean;
|
||||
}) {
|
||||
const { data, withServer = true, canCopy = true } = props;
|
||||
const { data, context, withServer = true, canCopy = true } = props;
|
||||
const { method, path, operation } = data;
|
||||
|
||||
const server = getDefaultServerURL(data.servers);
|
||||
@@ -41,6 +43,7 @@ export function OpenAPIPath(props: {
|
||||
className="openapi-path-title"
|
||||
data-deprecated={operation.deprecated}
|
||||
isDisabled={!canCopy}
|
||||
context={getOpenAPIClientContext(context)}
|
||||
>
|
||||
{element}
|
||||
</OpenAPICopyButton>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { InteractiveSection } from './InteractiveSection';
|
||||
import { OpenAPIRootSchema } from './OpenAPISchemaServer';
|
||||
import type { OpenAPIClientContext, OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import { checkIsReference, createStateKey } from './utils';
|
||||
|
||||
/**
|
||||
@@ -18,11 +20,9 @@ export function OpenAPIRequestBody(props: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const header = 'name' in data ? 'Payload' : 'Body';
|
||||
|
||||
return (
|
||||
<InteractiveSection
|
||||
header={header}
|
||||
header={t(context.translation, 'name' in data ? 'payload' : 'body')}
|
||||
className="openapi-requestbody"
|
||||
stateKey={createStateKey('request-body-media-type', context.blockKey)}
|
||||
selectIcon={context.icons.chevronDown}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
||||
import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
|
||||
import type { OpenAPIClientContext } from './types';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { parameterToProperty, resolveDescription } from './utils';
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,8 @@ import { Markdown } from './Markdown';
|
||||
import { OpenAPIEmptyExample, OpenAPIExample } from './OpenAPIExample';
|
||||
import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
|
||||
import { OpenAPIResponseExampleContent } from './OpenAPIResponseExampleContent';
|
||||
import type { OpenAPIContext, OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
||||
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import { getExampleFromReference, getExamples } from './util/example';
|
||||
import { createStateKey, getStatusCodeDefaultLabel } from './utils';
|
||||
import { checkIsReference, resolveDescription } from './utils';
|
||||
@@ -45,7 +46,7 @@ export function OpenAPIResponseExample(props: {
|
||||
const label = description ? (
|
||||
<Markdown source={description} />
|
||||
) : (
|
||||
getStatusCodeDefaultLabel(key)
|
||||
getStatusCodeDefaultLabel(key, context)
|
||||
);
|
||||
|
||||
if (checkIsReference(responseObject)) {
|
||||
@@ -55,7 +56,7 @@ export function OpenAPIResponseExample(props: {
|
||||
statusCode: key,
|
||||
body: (
|
||||
<OpenAPIExample
|
||||
example={getExampleFromReference(responseObject)}
|
||||
example={getExampleFromReference(responseObject, context)}
|
||||
context={context}
|
||||
syntax="json"
|
||||
/>
|
||||
@@ -68,7 +69,7 @@ export function OpenAPIResponseExample(props: {
|
||||
key: key,
|
||||
label,
|
||||
statusCode: key,
|
||||
body: <OpenAPIEmptyExample />,
|
||||
body: <OpenAPIEmptyExample context={context} />,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,6 +128,7 @@ function OpenAPIResponse(props: {
|
||||
selectIcon={context.icons.chevronDown}
|
||||
stateKey={createStateKey('response-media-types', context.blockKey)}
|
||||
items={tabs}
|
||||
context={getOpenAPIClientContext(context)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
|
||||
import { OpenAPIResponse } from './OpenAPIResponse';
|
||||
import { useResponseExamplesState } from './OpenAPIResponseExampleContent';
|
||||
import { StaticSection } from './StaticSection';
|
||||
import type { OpenAPIClientContext } from './types';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
import { createStateKey, getStatusCodeClassName, getStatusCodeDefaultLabel } from './utils';
|
||||
|
||||
/**
|
||||
@@ -76,7 +77,7 @@ export function OpenAPIResponses(props: {
|
||||
className="openapi-response-description"
|
||||
/>
|
||||
) : (
|
||||
getStatusCodeDefaultLabel(statusCode)
|
||||
getStatusCodeDefaultLabel(statusCode, context)
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
@@ -88,7 +89,7 @@ export function OpenAPIResponses(props: {
|
||||
const state = useResponseExamplesState(context.blockKey, groups[0]?.key);
|
||||
|
||||
return (
|
||||
<StaticSection header="Responses" className="openapi-responses">
|
||||
<StaticSection header={t(context.translation, 'responses')} className="openapi-responses">
|
||||
<OpenAPIDisclosureGroup
|
||||
icon={context.icons.chevronRight}
|
||||
expandedKeys={state.key ? new Set([state.key]) : new Set()}
|
||||
|
||||
@@ -10,9 +10,10 @@ import { Markdown } from './Markdown';
|
||||
import { OpenAPICopyButton } from './OpenAPICopyButton';
|
||||
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
||||
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { retrocycle } from './decycle';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import type { OpenAPIClientContext } from './types';
|
||||
import { tString } from './translate';
|
||||
import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
|
||||
|
||||
type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
|
||||
@@ -40,7 +41,7 @@ function OpenAPISchemaProperty(props: {
|
||||
|
||||
return (
|
||||
<div id={id} className={clsx('openapi-schema', className)}>
|
||||
<OpenAPISchemaPresentation property={property} />
|
||||
<OpenAPISchemaPresentation context={context} property={property} />
|
||||
{(() => {
|
||||
const circularRefId = parentCircularRefs.get(schema);
|
||||
// Avoid recursing infinitely, and instead render a link to the parent schema
|
||||
@@ -56,7 +57,9 @@ function OpenAPISchemaProperty(props: {
|
||||
return (
|
||||
<OpenAPIDisclosure
|
||||
icon={context.icons.plus}
|
||||
label={(isExpanded) => getDisclosureLabel(schema, isExpanded)}
|
||||
label={(isExpanded) =>
|
||||
getDisclosureLabel({ schema, isExpanded, context })
|
||||
}
|
||||
>
|
||||
<OpenAPISchemaProperties
|
||||
properties={properties}
|
||||
@@ -213,7 +216,7 @@ function OpenAPISchemaAlternative(props: {
|
||||
) : null}
|
||||
<OpenAPIDisclosure
|
||||
icon={context.icons.plus}
|
||||
label={(isExpanded) => getDisclosureLabel(schema, isExpanded)}
|
||||
label={(isExpanded) => getDisclosureLabel({ schema, isExpanded, context })}
|
||||
>
|
||||
{properties?.length ? (
|
||||
<OpenAPISchemaProperties
|
||||
@@ -252,8 +255,9 @@ function OpenAPISchemaCircularRef(props: { id: string; schema: OpenAPIV3.SchemaO
|
||||
*/
|
||||
function OpenAPISchemaEnum(props: {
|
||||
schema: OpenAPIV3.SchemaObject & OpenAPICustomOperationProperties;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { schema } = props;
|
||||
const { schema, context } = props;
|
||||
|
||||
const enumValues = (() => {
|
||||
// Render x-gitbook-enum first, as it has a different format
|
||||
@@ -296,6 +300,7 @@ function OpenAPISchemaEnum(props: {
|
||||
value={item.value}
|
||||
label={item.description}
|
||||
withTooltip={!!item.description}
|
||||
context={context}
|
||||
>
|
||||
<code>{`${item.value}`}</code>
|
||||
</OpenAPICopyButton>
|
||||
@@ -308,9 +313,13 @@ function OpenAPISchemaEnum(props: {
|
||||
/**
|
||||
* Render the top row of a schema. e.g: name, type, and required status.
|
||||
*/
|
||||
function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry }) {
|
||||
function OpenAPISchemaPresentation(props: {
|
||||
property: OpenAPISchemaPropertyEntry;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const {
|
||||
property: { schema, propertyName, required },
|
||||
context,
|
||||
} = props;
|
||||
|
||||
const description = resolveDescription(schema);
|
||||
@@ -323,6 +332,7 @@ function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry
|
||||
type={getSchemaTitle(schema)}
|
||||
propertyName={propertyName}
|
||||
required={required}
|
||||
context={context}
|
||||
/>
|
||||
{typeof schema['x-deprecated-sunset'] === 'string' ? (
|
||||
<div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
||||
@@ -355,7 +365,7 @@ function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry
|
||||
Pattern: <code>{schema.pattern}</code>
|
||||
</span>
|
||||
) : null}
|
||||
<OpenAPISchemaEnum schema={schema} />
|
||||
<OpenAPISchemaEnum schema={schema} context={context} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -592,21 +602,26 @@ function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
|
||||
return type;
|
||||
}
|
||||
|
||||
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject, isExpanded: boolean) {
|
||||
function getDisclosureLabel(props: {
|
||||
schema: OpenAPIV3.SchemaObject;
|
||||
isExpanded: boolean;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { schema, isExpanded, context } = props;
|
||||
let label: string;
|
||||
if (schema.type === 'array' && !!schema.items) {
|
||||
if (schema.items.oneOf) {
|
||||
label = 'available items';
|
||||
label = tString(context.translation, 'available_items').toLowerCase();
|
||||
}
|
||||
// Fallback to "child attributes" for enums and objects
|
||||
else if (schema.items.enum || schema.items.type === 'object') {
|
||||
label = 'child attributes';
|
||||
label = tString(context.translation, 'child_attributes').toLowerCase();
|
||||
} else {
|
||||
label = schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
|
||||
}
|
||||
} else {
|
||||
label = schema.title || 'child attributes';
|
||||
label = schema.title || tString(context.translation, 'child_attributes').toLowerCase();
|
||||
}
|
||||
|
||||
return `${isExpanded ? 'Hide' : 'Show'} ${label}`;
|
||||
return `${isExpanded ? tString(context.translation, 'hide') : tString(context.translation, 'show')} ${label}`;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import type React from 'react';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t, tString } from './translate';
|
||||
|
||||
interface OpenAPISchemaNameProps {
|
||||
schema?: OpenAPIV3.SchemaObject;
|
||||
propertyName?: string | React.JSX.Element;
|
||||
required?: boolean;
|
||||
type?: string;
|
||||
context: OpenAPIClientContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,9 +16,9 @@ interface OpenAPISchemaNameProps {
|
||||
* It includes the property name, type, required and deprecated status.
|
||||
*/
|
||||
export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
|
||||
const { schema, type, propertyName, required } = props;
|
||||
const { schema, type, propertyName, required, context } = props;
|
||||
|
||||
const additionalItems = schema && getAdditionalItems(schema);
|
||||
const additionalItems = schema && getAdditionalItems(schema, context);
|
||||
|
||||
return (
|
||||
<span className="openapi-schema-name">
|
||||
@@ -30,33 +33,45 @@ export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
|
||||
<span className="openapi-schema-type">{additionalItems}</span>
|
||||
) : null}
|
||||
</span>
|
||||
{schema?.readOnly ? <span className="openapi-schema-readonly">read-only</span> : null}
|
||||
{schema?.readOnly ? (
|
||||
<span className="openapi-schema-readonly">
|
||||
{t(context.translation, 'read_only')}
|
||||
</span>
|
||||
) : null}
|
||||
{schema?.writeOnly ? (
|
||||
<span className="openapi-schema-writeonly">write-only</span>
|
||||
<span className="openapi-schema-writeonly">
|
||||
{t(context.translation, 'write_only')}
|
||||
</span>
|
||||
) : null}
|
||||
{required ? (
|
||||
<span className="openapi-schema-required">required</span>
|
||||
<span className="openapi-schema-required">
|
||||
{t(context.translation, 'required')}
|
||||
</span>
|
||||
) : (
|
||||
<span className="openapi-schema-optional">optional</span>
|
||||
<span className="openapi-schema-optional">
|
||||
{t(context.translation, 'optional')}
|
||||
</span>
|
||||
)}
|
||||
{schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
|
||||
{schema?.deprecated ? (
|
||||
<span className="openapi-deprecated">{t(context.translation, 'deprecated')}</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
|
||||
function getAdditionalItems(schema: OpenAPIV3.SchemaObject, context: OpenAPIClientContext): string {
|
||||
let additionalItems = '';
|
||||
|
||||
if (schema.minimum || schema.minLength || schema.minItems) {
|
||||
additionalItems += ` · min: ${schema.minimum || schema.minLength || schema.minItems}`;
|
||||
additionalItems += ` · ${tString(context.translation, 'min').toLowerCase()}: ${schema.minimum || schema.minLength || schema.minItems}`;
|
||||
}
|
||||
|
||||
if (schema.maximum || schema.maxLength || schema.maxItems) {
|
||||
additionalItems += ` · max: ${schema.maximum || schema.maxLength || schema.maxItems}`;
|
||||
additionalItems += ` · ${tString(context.translation, 'max').toLowerCase()}: ${schema.maximum || schema.maxLength || schema.maxItems}`;
|
||||
}
|
||||
|
||||
if (schema.nullable) {
|
||||
additionalItems = ' | nullable';
|
||||
additionalItems = ` | ${tString(context.translation, 'nullable').toLowerCase()}`;
|
||||
}
|
||||
|
||||
return additionalItems;
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
OpenAPISchemaPropertiesFromServer,
|
||||
type OpenAPISchemaPropertyEntry,
|
||||
} from './OpenAPISchema';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { decycle } from './decycle';
|
||||
import type { OpenAPIClientContext } from './types';
|
||||
|
||||
export function OpenAPISchemaProperties(props: {
|
||||
id?: string;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { InteractiveSection } from './InteractiveSection';
|
||||
import { Markdown } from './Markdown';
|
||||
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
||||
import type {
|
||||
OpenAPIClientContext,
|
||||
OpenAPIOperationData,
|
||||
OpenAPISecurityWithRequired,
|
||||
} from './types';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
import type { OpenAPIOperationData, OpenAPISecurityWithRequired } from './types';
|
||||
import { createStateKey, resolveDescription } from './utils';
|
||||
|
||||
/**
|
||||
@@ -23,7 +21,7 @@ export function OpenAPISecurities(props: {
|
||||
|
||||
return (
|
||||
<InteractiveSection
|
||||
header="Authorizations"
|
||||
header={t(context.translation, 'authorizations')}
|
||||
stateKey={createStateKey('securities', context.blockKey)}
|
||||
toggeable
|
||||
defaultOpened={false}
|
||||
@@ -38,7 +36,7 @@ export function OpenAPISecurities(props: {
|
||||
body: (
|
||||
<div className="openapi-schema">
|
||||
<div className="openapi-schema-presentation">
|
||||
{getLabelForType(security)}
|
||||
{getLabelForType(security, context)}
|
||||
|
||||
{description ? (
|
||||
<Markdown
|
||||
@@ -55,11 +53,12 @@ export function OpenAPISecurities(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function getLabelForType(security: OpenAPISecurityWithRequired) {
|
||||
function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPIClientContext) {
|
||||
switch (security.type) {
|
||||
case 'apiKey':
|
||||
return (
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName={security.name ?? 'apiKey'}
|
||||
type="string"
|
||||
required={security.required}
|
||||
@@ -69,6 +68,7 @@ function getLabelForType(security: OpenAPISecurityWithRequired) {
|
||||
if (security.scheme === 'basic') {
|
||||
return (
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName="Authorization"
|
||||
type="string"
|
||||
required={security.required}
|
||||
@@ -81,6 +81,7 @@ function getLabelForType(security: OpenAPISecurityWithRequired) {
|
||||
return (
|
||||
<>
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName="Authorization"
|
||||
type="string"
|
||||
required={security.required}
|
||||
@@ -96,11 +97,29 @@ function getLabelForType(security: OpenAPISecurityWithRequired) {
|
||||
);
|
||||
}
|
||||
|
||||
return <OpenAPISchemaName propertyName="HTTP" required={security.required} />;
|
||||
return (
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName="HTTP"
|
||||
required={security.required}
|
||||
/>
|
||||
);
|
||||
case 'oauth2':
|
||||
return <OpenAPISchemaName propertyName="OAuth2" required={security.required} />;
|
||||
return (
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName="OAuth2"
|
||||
required={security.required}
|
||||
/>
|
||||
);
|
||||
case 'openIdConnect':
|
||||
return <OpenAPISchemaName propertyName="OpenID Connect" required={security.required} />;
|
||||
return (
|
||||
<OpenAPISchemaName
|
||||
context={context}
|
||||
propertyName="OpenID Connect"
|
||||
required={security.required}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
// @ts-ignore
|
||||
return security.type;
|
||||
|
||||
@@ -5,7 +5,9 @@ import { OpenAPIResponses } from './OpenAPIResponses';
|
||||
import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
|
||||
import { OpenAPISecurities } from './OpenAPISecurities';
|
||||
import { StaticSection } from './StaticSection';
|
||||
import type { OpenAPIClientContext, OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { tString } from './translate';
|
||||
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
import { parameterToProperty } from './utils';
|
||||
|
||||
export function OpenAPISpec(props: {
|
||||
@@ -17,7 +19,7 @@ export function OpenAPISpec(props: {
|
||||
const { operation } = data;
|
||||
|
||||
const parameters = operation.parameters ?? [];
|
||||
const parameterGroups = groupParameters(parameters);
|
||||
const parameterGroups = groupParameters(parameters, context);
|
||||
|
||||
const securities = 'securities' in data ? data.securities : [];
|
||||
|
||||
@@ -61,7 +63,10 @@ export function OpenAPISpec(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function groupParameters(parameters: OpenAPI.Parameters): Array<{
|
||||
function groupParameters(
|
||||
parameters: OpenAPI.Parameters,
|
||||
context: OpenAPIClientContext
|
||||
): Array<{
|
||||
key: string;
|
||||
label: string;
|
||||
parameters: OpenAPI.Parameters;
|
||||
@@ -78,7 +83,7 @@ function groupParameters(parameters: OpenAPI.Parameters): Array<{
|
||||
.filter((parameter) => parameter.in)
|
||||
.forEach((parameter) => {
|
||||
const key = parameter.in;
|
||||
const label = getParameterGroupName(parameter.in);
|
||||
const label = getParameterGroupName(parameter.in, context);
|
||||
const group = groups.find((group) => group.key === key);
|
||||
if (group) {
|
||||
group.parameters.push(parameter);
|
||||
@@ -96,14 +101,14 @@ function groupParameters(parameters: OpenAPI.Parameters): Array<{
|
||||
return groups;
|
||||
}
|
||||
|
||||
function getParameterGroupName(paramIn: string): string {
|
||||
function getParameterGroupName(paramIn: string, context: OpenAPIClientContext): string {
|
||||
switch (paramIn) {
|
||||
case 'path':
|
||||
return 'Path parameters';
|
||||
return tString(context.translation, 'path_parameters');
|
||||
case 'query':
|
||||
return 'Query parameters';
|
||||
return tString(context.translation, 'query_parameters');
|
||||
case 'header':
|
||||
return 'Header parameters';
|
||||
return tString(context.translation, 'header_parameters');
|
||||
default:
|
||||
return paramIn;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import clsx from 'clsx';
|
||||
import { OpenAPIWebhookExample } from './OpenAPIWebhookExample';
|
||||
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
|
||||
import { OpenAPISummary } from './common/OpenAPISummary';
|
||||
import type { OpenAPIContext, OpenAPIWebhookData } from './types';
|
||||
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
|
||||
import type { OpenAPIWebhookData } from './types';
|
||||
|
||||
/**
|
||||
* Display an interactive OpenAPI webhook.
|
||||
@@ -10,9 +11,11 @@ import type { OpenAPIContext, OpenAPIWebhookData } from './types';
|
||||
export function OpenAPIWebhook(props: {
|
||||
className?: string;
|
||||
data: OpenAPIWebhookData;
|
||||
context: OpenAPIContext;
|
||||
context: OpenAPIContextInput;
|
||||
}) {
|
||||
const { className, data, context } = props;
|
||||
const { className, data, context: contextInput } = props;
|
||||
|
||||
const context = resolveOpenAPIContext(contextInput);
|
||||
|
||||
return (
|
||||
<div className={clsx('openapi-webhook', className)}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { OpenAPIEmptyExample } from './OpenAPIExample';
|
||||
import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
|
||||
import type { OpenAPIContext } from './types';
|
||||
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
||||
import type { OpenAPIWebhookData } from './types';
|
||||
import { getExamples } from './util/example';
|
||||
import { createStateKey } from './utils';
|
||||
@@ -27,7 +27,7 @@ export function OpenAPIWebhookExample(props: {
|
||||
return {
|
||||
key,
|
||||
label: key,
|
||||
body: <OpenAPIEmptyExample />,
|
||||
body: <OpenAPIEmptyExample context={context} />,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export function OpenAPIWebhookExample(props: {
|
||||
selectIcon={context.icons.chevronDown}
|
||||
stateKey={createStateKey('request-body-media-type', context.blockKey)}
|
||||
items={items}
|
||||
context={getOpenAPIClientContext(context)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,8 @@ import { createPortal } from 'react-dom';
|
||||
|
||||
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
||||
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
||||
import type { OpenAPIClientContext } from './context';
|
||||
import { t } from './translate';
|
||||
|
||||
/**
|
||||
* Button which launches the Scalar API Client
|
||||
@@ -14,8 +16,9 @@ export function ScalarApiButton(props: {
|
||||
method: OpenAPIV3_1.HttpMethods;
|
||||
path: string;
|
||||
specUrl: string;
|
||||
context: OpenAPIClientContext;
|
||||
}) {
|
||||
const { method, path, specUrl } = props;
|
||||
const { method, path, specUrl, context } = props;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const controllerRef = useRef<ScalarModalControllerRef>(null);
|
||||
return (
|
||||
@@ -27,7 +30,7 @@ export function ScalarApiButton(props: {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
Test it
|
||||
{t(context.translation, 'test_it')}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
|
||||
<path
|
||||
stroke="currentColor"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OpenAPISpec } from '../OpenAPISpec';
|
||||
import { getOpenAPIClientContext } from '../context';
|
||||
import type { OpenAPIContext, OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
||||
import { type OpenAPIContext, getOpenAPIClientContext } from '../context';
|
||||
import { t } from '../translate';
|
||||
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
||||
import { OpenAPIOperationDescription } from './OpenAPIOperationDescription';
|
||||
|
||||
export function OpenAPIColumnSpec(props: {
|
||||
@@ -16,11 +17,11 @@ export function OpenAPIColumnSpec(props: {
|
||||
<div className="openapi-column-spec">
|
||||
{operation['x-deprecated-sunset'] ? (
|
||||
<div className="openapi-deprecated-sunset openapi-description openapi-markdown">
|
||||
This operation is deprecated and will be sunset on{' '}
|
||||
<span className="openapi-deprecated-sunset-date">
|
||||
{operation['x-deprecated-sunset']}
|
||||
</span>
|
||||
{'.'}
|
||||
{t(context.translation, 'deprecated_and_sunset_on', [
|
||||
<span key="date" className="openapi-deprecated-sunset-date">
|
||||
{operation['x-deprecated-sunset']}
|
||||
</span>,
|
||||
])}
|
||||
</div>
|
||||
) : null}
|
||||
<OpenAPIOperationDescription operation={operation} context={context} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { Markdown } from '../Markdown';
|
||||
import type { OpenAPIContext } from '../types';
|
||||
import type { OpenAPIContext } from '../context';
|
||||
import { resolveDescription } from '../utils';
|
||||
|
||||
export function OpenAPIOperationDescription(props: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { OpenAPIPath } from '../OpenAPIPath';
|
||||
import type { OpenAPIContext, OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
||||
import type { OpenAPIContext } from '../context';
|
||||
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
||||
import { OpenAPIStability } from './OpenAPIStability';
|
||||
|
||||
export function OpenAPISummary(props: {
|
||||
@@ -38,7 +39,7 @@ export function OpenAPISummary(props: {
|
||||
title,
|
||||
})
|
||||
: null}
|
||||
{'path' in data ? <OpenAPIPath data={data} /> : null}
|
||||
{'path' in data ? <OpenAPIPath data={data} context={context} /> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { type Translation, type TranslationLocale, translations } from './translations';
|
||||
|
||||
export interface OpenAPIClientContext {
|
||||
/**
|
||||
* The translation language to use.
|
||||
*/
|
||||
translation: Translation;
|
||||
|
||||
/**
|
||||
* Icons used in the block.
|
||||
*/
|
||||
@@ -23,9 +30,14 @@ export interface OpenAPIClientContext {
|
||||
* Optional id attached to the heading and used as an anchor.
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Mark the context as a client context.
|
||||
*/
|
||||
$$isClientContext$$: true;
|
||||
}
|
||||
|
||||
export interface OpenAPIContext extends OpenAPIClientContext {
|
||||
export interface OpenAPIContext extends Omit<OpenAPIClientContext, '$$isClientContext$$'> {
|
||||
/**
|
||||
* Render a code block.
|
||||
*/
|
||||
@@ -51,14 +63,37 @@ export interface OpenAPIContext extends OpenAPIClientContext {
|
||||
specUrl: string;
|
||||
}
|
||||
|
||||
export type OpenAPIUniversalContext = OpenAPIClientContext | OpenAPIContext;
|
||||
|
||||
export interface OpenAPIContextInput extends Omit<OpenAPIContext, 'translation'> {
|
||||
/**
|
||||
* The translation language to use.
|
||||
* @default 'en'
|
||||
*/
|
||||
locale?: TranslationLocale | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve OpenAPI context from the input.
|
||||
*/
|
||||
export function resolveOpenAPIContext(context: OpenAPIContextInput): OpenAPIContext {
|
||||
const { locale, ...rest } = context;
|
||||
return {
|
||||
...rest,
|
||||
translation: translations[locale ?? 'en'],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client context from the OpenAPI context.
|
||||
*/
|
||||
export function getOpenAPIClientContext(context: OpenAPIContext): OpenAPIClientContext {
|
||||
export function getOpenAPIClientContext(context: OpenAPIUniversalContext): OpenAPIClientContext {
|
||||
return {
|
||||
translation: context.translation,
|
||||
icons: context.icons,
|
||||
defaultInteractiveOpened: context.defaultInteractiveOpened,
|
||||
blockKey: context.blockKey,
|
||||
id: context.id,
|
||||
$$isClientContext$$: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,4 +4,6 @@ export * from './OpenAPIWebhook';
|
||||
export * from './OpenAPIOperationContext';
|
||||
export * from './resolveOpenAPIOperation';
|
||||
export * from './resolveOpenAPIWebhook';
|
||||
export type { OpenAPIOperationData, OpenAPIContext, OpenAPIWebhookData } from './types';
|
||||
export type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
||||
export type { OpenAPIContextInput } from './context';
|
||||
export { checkIsValidLocale } from './translations';
|
||||
|
||||
@@ -4,8 +4,12 @@ import { OpenAPIDisclosure } from '../OpenAPIDisclosure';
|
||||
import { OpenAPIExample } from '../OpenAPIExample';
|
||||
import { OpenAPIRootSchema } from '../OpenAPISchemaServer';
|
||||
import { Section, SectionBody, StaticSection } from '../StaticSection';
|
||||
import { getOpenAPIClientContext } from '../context';
|
||||
import type { OpenAPIContext } from '../types';
|
||||
import {
|
||||
type OpenAPIContextInput,
|
||||
getOpenAPIClientContext,
|
||||
resolveOpenAPIContext,
|
||||
} from '../context';
|
||||
import { t } from '../translate';
|
||||
import { getExampleFromSchema } from '../util/example';
|
||||
|
||||
/**
|
||||
@@ -14,13 +18,13 @@ import { getExampleFromSchema } from '../util/example';
|
||||
export function OpenAPISchemas(props: {
|
||||
className?: string;
|
||||
schemas: OpenAPISchema[];
|
||||
context: OpenAPIContext;
|
||||
context: OpenAPIContextInput;
|
||||
/**
|
||||
* Whether to show the schema directly if there is only one.
|
||||
*/
|
||||
grouped?: boolean;
|
||||
}) {
|
||||
const { schemas, context, grouped, className } = props;
|
||||
const { schemas, context: contextInput, grouped, className } = props;
|
||||
|
||||
const firstSchema = schemas[0];
|
||||
|
||||
@@ -28,6 +32,7 @@ export function OpenAPISchemas(props: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const context = resolveOpenAPIContext(contextInput);
|
||||
const clientContext = getOpenAPIClientContext(context);
|
||||
|
||||
// If there is only one model and we are not grouping, we show it directly.
|
||||
@@ -38,11 +43,16 @@ export function OpenAPISchemas(props: {
|
||||
<div className="openapi-summary" id={context.id}>
|
||||
{context.renderHeading({
|
||||
title,
|
||||
deprecated: Boolean(firstSchema.schema.deprecated),
|
||||
stability: firstSchema.schema['x-stability'],
|
||||
})}
|
||||
</div>
|
||||
<div className="openapi-columns">
|
||||
<div className="openapi-column-spec">
|
||||
<StaticSection className="openapi-parameters" header="Attributes">
|
||||
<StaticSection
|
||||
className="openapi-parameters"
|
||||
header={t(context.translation, 'attributes')}
|
||||
>
|
||||
<OpenAPIRootSchema
|
||||
schema={firstSchema.schema}
|
||||
context={clientContext}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { Translation, TranslationKey } from './translations';
|
||||
|
||||
/**
|
||||
* Translate a string.
|
||||
*/
|
||||
export function t(
|
||||
translation: Translation,
|
||||
id: TranslationKey,
|
||||
...args: React.ReactNode[]
|
||||
): React.ReactNode {
|
||||
const string = translation[id];
|
||||
if (!string) {
|
||||
throw new Error(`Translation not found for "${id}"`);
|
||||
}
|
||||
|
||||
// Now we are going to replace the arguments
|
||||
// but we want to return a string as long as it's possible
|
||||
// (eg. if there isn't any argument that is a ReactNode)
|
||||
const parts: React.ReactNode[] = [];
|
||||
let currentStringToReplace: string = string;
|
||||
|
||||
args.forEach((arg, i) => {
|
||||
if (typeof arg === 'string') {
|
||||
currentStringToReplace = currentStringToReplace.replace(`\${${i + 1}}`, arg);
|
||||
} else {
|
||||
const [partToPush, partToReplace] = currentStringToReplace.split(`\${${i + 1}}`);
|
||||
if (partToPush === undefined || partToReplace === undefined) {
|
||||
throw new Error(`Invalid translation "${id}"`);
|
||||
}
|
||||
parts.push(<React.Fragment key={`string-${i}`}>{partToPush}</React.Fragment>);
|
||||
parts.push(<React.Fragment key={`arg-${i}`}>{arg}</React.Fragment>);
|
||||
currentStringToReplace = partToReplace;
|
||||
}
|
||||
});
|
||||
|
||||
if (!parts.length) {
|
||||
return currentStringToReplace;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{parts}
|
||||
{currentStringToReplace}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of `t` that returns a string.
|
||||
*/
|
||||
export function tString(
|
||||
translation: Translation,
|
||||
id: TranslationKey,
|
||||
...args: React.ReactNode[]
|
||||
): string {
|
||||
const result = t(translation, id, ...args);
|
||||
return reactToString(result);
|
||||
}
|
||||
|
||||
function reactToString(el: React.ReactNode): string {
|
||||
if (typeof el === 'string' || typeof el === 'number' || typeof el === 'boolean') {
|
||||
return `${el}`;
|
||||
}
|
||||
|
||||
if (el === null || el === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (Array.isArray(el)) {
|
||||
return el.map(reactToString).join('');
|
||||
}
|
||||
|
||||
if (typeof el === 'object' && 'props' in el) {
|
||||
return el.props.children.map(reactToString).join('');
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported type ${typeof el}`);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export const de = {
|
||||
required: 'Erforderlich',
|
||||
deprecated: 'Veraltet',
|
||||
deprecated_and_sunset_on: 'Diese Operation ist veraltet und wird am ${1} eingestellt.',
|
||||
stability_experimental: 'Experimentell',
|
||||
stability_alpha: 'Alpha',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: 'In die Zwischenablage kopieren',
|
||||
copied: 'Kopiert',
|
||||
no_content: 'Kein Inhalt',
|
||||
unresolved_reference: 'Nicht aufgelöste Referenz',
|
||||
circular_reference: 'Zirkuläre Referenz',
|
||||
read_only: 'Nur lesen',
|
||||
write_only: 'Nur schreiben',
|
||||
optional: 'Optional',
|
||||
min: 'Min',
|
||||
max: 'Max',
|
||||
nullable: 'Nullfähig',
|
||||
body: 'Rumpf',
|
||||
payload: 'Nutzlast',
|
||||
headers: 'Kopfzeilen',
|
||||
authorizations: 'Autorisierungen',
|
||||
responses: 'Antworten',
|
||||
path_parameters: 'Pfadparameter',
|
||||
query_parameters: 'Abfrageparameter',
|
||||
header_parameters: 'Header-Parameter',
|
||||
attributes: 'Attribute',
|
||||
test_it: 'Teste es',
|
||||
information: 'Information',
|
||||
success: 'Erfolg',
|
||||
redirect: 'Umleitung',
|
||||
error: 'Fehler',
|
||||
show: 'Anzeigen',
|
||||
hide: 'Verstecken',
|
||||
available_items: 'Verfügbare Elemente',
|
||||
child_attributes: 'Unterattribute',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const en = {
|
||||
required: 'Required',
|
||||
deprecated: 'Deprecated',
|
||||
deprecated_and_sunset_on: 'This operation is deprecated and will be sunset on ${1}.',
|
||||
stability_experimental: 'Experimental',
|
||||
stability_alpha: 'Alpha',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: 'Copy to clipboard',
|
||||
copied: 'Copied',
|
||||
no_content: 'No content',
|
||||
unresolved_reference: 'Unresolved reference',
|
||||
circular_reference: 'Circular reference',
|
||||
read_only: 'Read-only',
|
||||
write_only: 'Write-only',
|
||||
optional: 'Optional',
|
||||
min: 'Min',
|
||||
max: 'Max',
|
||||
nullable: 'Nullable',
|
||||
body: 'Body',
|
||||
payload: 'Payload',
|
||||
headers: 'Headers',
|
||||
authorizations: 'Authorizations',
|
||||
responses: 'Responses',
|
||||
path_parameters: 'Path parameters',
|
||||
query_parameters: 'Query parameters',
|
||||
header_parameters: 'Header parameters',
|
||||
attributes: 'Attributes',
|
||||
test_it: 'Test it',
|
||||
information: 'Information',
|
||||
success: 'Success',
|
||||
redirect: 'Redirect',
|
||||
error: 'Error',
|
||||
show: 'Show',
|
||||
hide: 'Hide',
|
||||
available_items: 'Available items',
|
||||
child_attributes: 'Child attributes',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const es = {
|
||||
required: 'Requerido',
|
||||
deprecated: 'Obsoleto',
|
||||
deprecated_and_sunset_on: 'Esta operación está obsoleta y se retirará el ${1}.',
|
||||
stability_experimental: 'Experimental',
|
||||
stability_alpha: 'Alfa',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: 'Copiar al portapapeles',
|
||||
copied: 'Copiado',
|
||||
no_content: 'Sin contenido',
|
||||
unresolved_reference: 'Referencia no resuelta',
|
||||
circular_reference: 'Referencia circular',
|
||||
read_only: 'Solo lectura',
|
||||
write_only: 'Solo escritura',
|
||||
optional: 'Opcional',
|
||||
min: 'Mín',
|
||||
max: 'Máx',
|
||||
nullable: 'Nulo',
|
||||
body: 'Cuerpo',
|
||||
payload: 'Caga útil',
|
||||
headers: 'Encabezados',
|
||||
authorizations: 'Autorizaciones',
|
||||
responses: 'Respuestas',
|
||||
path_parameters: 'Parámetros de ruta',
|
||||
query_parameters: 'Parámetros de consulta',
|
||||
header_parameters: 'Parámetros de encabezado',
|
||||
attributes: 'Atributos',
|
||||
test_it: 'Pruébalo',
|
||||
information: 'Información',
|
||||
success: 'Éxito',
|
||||
redirect: 'Redirección',
|
||||
error: 'Error',
|
||||
show: 'Mostrar',
|
||||
hide: 'Ocultar',
|
||||
available_items: 'Elementos disponibles',
|
||||
child_attributes: 'Atributos secundarios',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const fr = {
|
||||
required: 'Requis',
|
||||
deprecated: 'Obsolète',
|
||||
deprecated_and_sunset_on: 'Cette opération est obsolète et sera supprimée le ${1}.',
|
||||
stability_experimental: 'Expérimental',
|
||||
stability_alpha: 'Alpha',
|
||||
stability_beta: 'Bêta',
|
||||
copy_to_clipboard: 'Copier dans le presse-papiers',
|
||||
copied: 'Copié',
|
||||
no_content: 'Aucun contenu',
|
||||
unresolved_reference: 'Référence non résolue',
|
||||
circular_reference: 'Référence circulaire',
|
||||
read_only: 'Lecture seule',
|
||||
write_only: 'Écriture seule',
|
||||
optional: 'Optionnel',
|
||||
min: 'Min',
|
||||
max: 'Max',
|
||||
nullable: 'Nullable',
|
||||
body: 'Corps',
|
||||
payload: 'Charge utile',
|
||||
headers: 'En-têtes',
|
||||
authorizations: 'Autorisations',
|
||||
responses: 'Réponses',
|
||||
path_parameters: 'Paramètres de chemin',
|
||||
query_parameters: 'Paramètres de requête',
|
||||
header_parameters: 'Paramètres d’en-tête',
|
||||
attributes: 'Attributs',
|
||||
test_it: 'Tester',
|
||||
information: 'Information',
|
||||
success: 'Succès',
|
||||
redirect: 'Redirection',
|
||||
error: 'Erreur',
|
||||
show: 'Afficher',
|
||||
hide: 'Masquer',
|
||||
available_items: 'Éléments disponibles',
|
||||
child_attributes: 'Attributs enfants',
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { de } from './de';
|
||||
import { en } from './en';
|
||||
import { es } from './es';
|
||||
import { fr } from './fr';
|
||||
import { ja } from './ja';
|
||||
import { nl } from './nl';
|
||||
import { no } from './no';
|
||||
import { pt_br } from './pt-br';
|
||||
import type { Translation } from './types';
|
||||
import { zh } from './zh';
|
||||
|
||||
export * from './types';
|
||||
|
||||
export const translations = {
|
||||
en,
|
||||
de,
|
||||
es,
|
||||
fr,
|
||||
ja,
|
||||
nl,
|
||||
no,
|
||||
'pt-br': pt_br,
|
||||
zh,
|
||||
} satisfies Record<string, Translation>;
|
||||
|
||||
export type TranslationLocale = keyof typeof translations;
|
||||
|
||||
/**
|
||||
* Check if the locale is valid.
|
||||
*/
|
||||
export function checkIsValidLocale(locale: string): locale is TranslationLocale {
|
||||
return Object.prototype.hasOwnProperty.call(translations, locale);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export const ja = {
|
||||
required: '必須',
|
||||
deprecated: '非推奨',
|
||||
deprecated_and_sunset_on: 'この操作は非推奨であり、${1}に廃止されます。',
|
||||
stability_experimental: '実験的',
|
||||
stability_alpha: 'アルファ',
|
||||
stability_beta: 'ベータ',
|
||||
copy_to_clipboard: 'クリップボードにコピー',
|
||||
copied: 'コピー済み',
|
||||
no_content: 'コンテンツなし',
|
||||
unresolved_reference: '未解決の参照',
|
||||
circular_reference: '循環参照',
|
||||
read_only: '読み取り専用',
|
||||
write_only: '書き込み専用',
|
||||
optional: 'オプション',
|
||||
min: '最小',
|
||||
max: '最大',
|
||||
nullable: 'ヌル許容',
|
||||
body: '本文',
|
||||
payload: 'ペイロード',
|
||||
headers: 'ヘッダー',
|
||||
authorizations: '認可',
|
||||
responses: 'レスポンス',
|
||||
path_parameters: 'パスパラメータ',
|
||||
query_parameters: 'クエリパラメータ',
|
||||
header_parameters: 'ヘッダーパラメータ',
|
||||
attributes: '属性',
|
||||
test_it: 'テストする',
|
||||
information: '情報',
|
||||
success: '成功',
|
||||
redirect: 'リダイレクト',
|
||||
error: 'エラー',
|
||||
show: '表示',
|
||||
hide: '非表示',
|
||||
available_items: '利用可能なアイテム',
|
||||
child_attributes: '子属性',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const nl = {
|
||||
required: 'Vereist',
|
||||
deprecated: 'Verouderd',
|
||||
deprecated_and_sunset_on: 'Deze bewerking is verouderd en wordt op ${1} beëindigd.',
|
||||
stability_experimental: 'Experimenteel',
|
||||
stability_alpha: 'Alfa',
|
||||
stability_beta: 'Bèta',
|
||||
copy_to_clipboard: 'Kopiëren naar klembord',
|
||||
copied: 'Gekopieerd',
|
||||
no_content: 'Geen inhoud',
|
||||
unresolved_reference: 'Onopgeloste verwijzing',
|
||||
circular_reference: 'Circulaire verwijzing',
|
||||
read_only: 'Alleen lezen',
|
||||
write_only: 'Alleen schrijven',
|
||||
optional: 'Optioneel',
|
||||
min: 'Min',
|
||||
max: 'Max',
|
||||
nullable: 'Null toegestaan',
|
||||
body: 'Body',
|
||||
payload: 'Payload',
|
||||
headers: 'Headers',
|
||||
authorizations: 'Autorisaties',
|
||||
responses: 'Reacties',
|
||||
path_parameters: 'Padparameters',
|
||||
query_parameters: 'Queryparameters',
|
||||
header_parameters: 'Headerparameters',
|
||||
attributes: 'Attributen',
|
||||
test_it: 'Test het',
|
||||
information: 'Informatie',
|
||||
success: 'Succes',
|
||||
redirect: 'Omleiding',
|
||||
error: 'Fout',
|
||||
show: 'Toon',
|
||||
hide: 'Verbergen',
|
||||
available_items: 'Beschikbare items',
|
||||
child_attributes: 'Kindattributen',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const no = {
|
||||
required: 'Påkrevd',
|
||||
deprecated: 'Foreldet',
|
||||
deprecated_and_sunset_on: 'Denne operasjonen er foreldet og vil bli avviklet den ${1}.',
|
||||
stability_experimental: 'Eksperimentell',
|
||||
stability_alpha: 'Alfa',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: 'Kopier til utklippstavle',
|
||||
copied: 'Kopiert',
|
||||
no_content: 'Ingen innhold',
|
||||
unresolved_reference: 'Uavklart referanse',
|
||||
circular_reference: 'Sirkulær referanse',
|
||||
read_only: 'Skrivebeskyttet',
|
||||
write_only: 'Kun skriving',
|
||||
optional: 'Valgfri',
|
||||
min: 'Min',
|
||||
max: 'Maks',
|
||||
nullable: 'Kan være null',
|
||||
body: 'Brødtekst',
|
||||
payload: 'Nyttelast',
|
||||
headers: 'Overskrifter',
|
||||
authorizations: 'Autorisasjoner',
|
||||
responses: 'Responser',
|
||||
path_parameters: 'Sti-parametere',
|
||||
query_parameters: 'Forespørselsparametere',
|
||||
header_parameters: 'Header-parametere',
|
||||
attributes: 'Attributter',
|
||||
test_it: 'Test det',
|
||||
information: 'Informasjon',
|
||||
success: 'Suksess',
|
||||
redirect: 'Viderekobling',
|
||||
error: 'Feil',
|
||||
show: 'Vis',
|
||||
hide: 'Skjul',
|
||||
available_items: 'Tilgjengelige elementer',
|
||||
child_attributes: 'Barneattributter',
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const pt_br = {
|
||||
required: 'Obrigatório',
|
||||
deprecated: 'Obsoleto',
|
||||
deprecated_and_sunset_on: 'Esta operação está obsoleta e será descontinuada em ${1}.',
|
||||
stability_experimental: 'Experimental',
|
||||
stability_alpha: 'Alfa',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: 'Copiar para a área de transferência',
|
||||
copied: 'Copiado',
|
||||
no_content: 'Sem conteúdo',
|
||||
unresolved_reference: 'Referência não resolvida',
|
||||
circular_reference: 'Referência circular',
|
||||
read_only: 'Somente leitura',
|
||||
write_only: 'Somente escrita',
|
||||
optional: 'Opcional',
|
||||
min: 'Mín',
|
||||
max: 'Máx',
|
||||
nullable: 'Nulo',
|
||||
body: 'Corpo',
|
||||
payload: 'Carga útil',
|
||||
headers: 'Cabeçalhos',
|
||||
authorizations: 'Autorizações',
|
||||
responses: 'Respostas',
|
||||
path_parameters: 'Parâmetros de rota',
|
||||
query_parameters: 'Parâmetros de consulta',
|
||||
header_parameters: 'Parâmetros de cabeçalho',
|
||||
attributes: 'Atributos',
|
||||
test_it: 'Testar',
|
||||
information: 'Informação',
|
||||
success: 'Sucesso',
|
||||
redirect: 'Redirecionamento',
|
||||
error: 'Erro',
|
||||
show: 'Mostrar',
|
||||
hide: 'Ocultar',
|
||||
available_items: 'Itens disponíveis',
|
||||
child_attributes: 'Atributos filhos',
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { en } from './en';
|
||||
|
||||
export type TranslationKey = keyof typeof en;
|
||||
|
||||
export type Translation = {
|
||||
[key in TranslationKey]: string;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
export const zh = {
|
||||
required: '必填',
|
||||
deprecated: '已弃用',
|
||||
deprecated_and_sunset_on: '此操作已弃用,将于 ${1} 停止使用。',
|
||||
stability_experimental: '实验性',
|
||||
stability_alpha: 'Alpha',
|
||||
stability_beta: 'Beta',
|
||||
copy_to_clipboard: '复制到剪贴板',
|
||||
copied: '已复制',
|
||||
no_content: '无内容',
|
||||
unresolved_reference: '未解析的引用',
|
||||
circular_reference: '循环引用',
|
||||
read_only: '只读',
|
||||
write_only: '只写',
|
||||
optional: '可选',
|
||||
min: '最小值',
|
||||
max: '最大值',
|
||||
nullable: '可为 null',
|
||||
body: '请求体',
|
||||
payload: '有效载荷',
|
||||
headers: '头部信息',
|
||||
authorizations: '授权',
|
||||
responses: '响应',
|
||||
path_parameters: '路径参数',
|
||||
query_parameters: '查询参数',
|
||||
header_parameters: '头参数',
|
||||
attributes: '属性',
|
||||
test_it: '测试一下',
|
||||
information: '信息',
|
||||
success: '成功',
|
||||
redirect: '重定向',
|
||||
error: '错误',
|
||||
show: '显示',
|
||||
hide: '隐藏',
|
||||
available_items: '可用项',
|
||||
child_attributes: '子属性',
|
||||
};
|
||||
@@ -4,59 +4,6 @@ import type {
|
||||
OpenAPIV3,
|
||||
} from '@gitbook/openapi-parser';
|
||||
|
||||
export interface OpenAPIClientContext {
|
||||
/**
|
||||
* Icons used in the block.
|
||||
*/
|
||||
icons: {
|
||||
chevronDown: React.ReactNode;
|
||||
chevronRight: React.ReactNode;
|
||||
plus: React.ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Force all sections to be opened by default.
|
||||
* @default false
|
||||
*/
|
||||
defaultInteractiveOpened?: boolean;
|
||||
|
||||
/**
|
||||
* The key of the block
|
||||
*/
|
||||
blockKey?: string;
|
||||
|
||||
/**
|
||||
* Optional id attached to the heading and used as an anchor.
|
||||
*/
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface OpenAPIContext extends OpenAPIClientContext {
|
||||
/**
|
||||
* Render a code block.
|
||||
*/
|
||||
renderCodeBlock: (props: { code: string; syntax: string }) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Render the heading of the operation.
|
||||
*/
|
||||
renderHeading: (props: {
|
||||
deprecated?: boolean;
|
||||
title: string;
|
||||
stability?: string;
|
||||
}) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Render the document of the operation.
|
||||
*/
|
||||
renderDocument: (props: { document: object }) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* Specification URL.
|
||||
*/
|
||||
specUrl: string;
|
||||
}
|
||||
|
||||
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject & { required?: boolean };
|
||||
|
||||
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
import { OpenAPIExample } from '../OpenAPIExample';
|
||||
import type { OpenAPIContext } from '../context';
|
||||
import { generateSchemaExample } from '../generateSchemaExample';
|
||||
import type { OpenAPIContext } from '../types';
|
||||
import { tString } from '../translate';
|
||||
import { checkIsReference } from '../utils';
|
||||
|
||||
/**
|
||||
* Generate an example from a reference object.
|
||||
*/
|
||||
export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
|
||||
return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
|
||||
export function getExampleFromReference(
|
||||
ref: OpenAPIV3.ReferenceObject,
|
||||
context: OpenAPIContext
|
||||
): OpenAPIV3.ExampleObject {
|
||||
return {
|
||||
summary: tString(context.translation, 'unresolved_reference'),
|
||||
value: { $ref: ref.$ref },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -17,13 +24,16 @@ export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPI
|
||||
export function getExamplesFromMediaTypeObject(args: {
|
||||
mediaType: string;
|
||||
mediaTypeObject: OpenAPIV3.MediaTypeObject;
|
||||
context: OpenAPIContext;
|
||||
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
|
||||
const { mediaTypeObject, mediaType } = args;
|
||||
const { mediaTypeObject, mediaType, context } = args;
|
||||
if (mediaTypeObject.examples) {
|
||||
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
|
||||
return {
|
||||
key,
|
||||
example: checkIsReference(example) ? getExampleFromReference(example) : example,
|
||||
example: checkIsReference(example)
|
||||
? getExampleFromReference(example, context)
|
||||
: example,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -88,8 +98,8 @@ export function getExamples(props: {
|
||||
mediaType: string;
|
||||
context: OpenAPIContext;
|
||||
}) {
|
||||
const { mediaTypeObject, mediaType } = props;
|
||||
const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType });
|
||||
const { mediaTypeObject, mediaType, context } = props;
|
||||
const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType, context });
|
||||
const syntax = getSyntaxFromMediaType(mediaType);
|
||||
|
||||
return examples.map((example) => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { AnyObject, OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
||||
import type { OpenAPIUniversalContext } from './context';
|
||||
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
||||
import { tString } from './translate';
|
||||
|
||||
export function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject {
|
||||
return typeof input === 'object' && !!input && '$ref' in input;
|
||||
@@ -182,18 +184,21 @@ export function getStatusCodeClassName(statusCode: number | string): string {
|
||||
* 3xx: Redirect
|
||||
* 4xx, 5xx: Error
|
||||
*/
|
||||
export function getStatusCodeDefaultLabel(statusCode: number | string): string {
|
||||
export function getStatusCodeDefaultLabel(
|
||||
statusCode: number | string,
|
||||
context: OpenAPIUniversalContext
|
||||
): string {
|
||||
const category = getStatusCodeCategory(statusCode);
|
||||
switch (category) {
|
||||
case 1:
|
||||
return 'Information';
|
||||
return tString(context.translation, 'information');
|
||||
case 2:
|
||||
return 'Success';
|
||||
return tString(context.translation, 'success');
|
||||
case 3:
|
||||
return 'Redirect';
|
||||
return tString(context.translation, 'redirect');
|
||||
case 4:
|
||||
case 5:
|
||||
return 'Error';
|
||||
return tString(context.translation, 'error');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user