diff --git a/package.json b/package.json index a2d2385b..50ec7589 100644 --- a/package.json +++ b/package.json @@ -79,5 +79,8 @@ "printWidth": 80, "tabWidth": 2 }, - "packageManager": "yarn@4.0.1" + "packageManager": "yarn@4.0.1", + "volta": { + "node": "18.12.0" + } } diff --git a/packages/docusaurus-plugin-redoc/src/index.ts b/packages/docusaurus-plugin-redoc/src/index.ts index b7d6991e..04564e14 100644 --- a/packages/docusaurus-plugin-redoc/src/index.ts +++ b/packages/docusaurus-plugin-redoc/src/index.ts @@ -24,7 +24,7 @@ import { PluginDirectUsageOptions, DEFAULT_OPTIONS, } from './options'; -import type { SpecProps, ApiDocProps } from './types/common'; +import type { SpecDataResult, ApiDocProps } from './types/common'; import { loadSpecWithConfig } from './loadSpec'; import { loadRedoclyConfig } from './loadRedoclyConfig'; @@ -33,6 +33,10 @@ const version = require('../package.json').version; export { PluginOptions, PluginDirectUsageOptions, loadRedoclyConfig }; +function getIsExternalUrl(url = '') { + return ['http://', 'https://'].some((protocol) => url.startsWith(protocol)); +} + export default function redocPlugin( context: LoadContext, opts: PluginOptions, @@ -45,12 +49,13 @@ export default function redocPlugin( const { debug, spec, url: downloadUrl, config, themeId } = options; let url = downloadUrl; - const isSpecFile = fs.existsSync(spec); + const isExternalUrl = getIsExternalUrl(url); + const fileName = path.join( 'redocusaurus', `${options.id || 'api-spec'}.yaml`, ); - let filesToWatch: string[] = isSpecFile ? [path.resolve(spec)] : []; + let filesToWatch: string[] = !isExternalUrl ? [path.resolve(spec)] : []; if (debug) { console.error('[REDOCUSAURUS_PLUGIN] Opts Input:', opts); @@ -67,7 +72,7 @@ export default function redocPlugin( let bundledSpec: Document, problems: NormalizedProblem[]; - if (!isSpecFile) { + if (isExternalUrl) { // If spec is a remote url then add it as download url also as a default url = url || spec; if (debug) { @@ -123,10 +128,9 @@ export default function redocPlugin( throw new Error(`[Redocusaurus] Spec could not be parsed: ${spec}`); } - const data: SpecProps = { + const data: SpecDataResult = { url, themeId, - isSpecFile, // eslint-disable-next-line @typescript-eslint/no-explicit-any spec: content.converted as any, }; @@ -165,7 +169,7 @@ export default function redocPlugin( } }, async postBuild({ content }) { - if (!isSpecFile || downloadUrl) { + if (isExternalUrl || downloadUrl) { return; } // Create a static file from bundled spec diff --git a/packages/docusaurus-theme-redoc/src/theme/ApiDocMdx/ApiDocMdx.tsx b/packages/docusaurus-theme-redoc/src/theme/ApiDocMdx/ApiDocMdx.tsx index a6e69c17..d4f29394 100644 --- a/packages/docusaurus-theme-redoc/src/theme/ApiDocMdx/ApiDocMdx.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/ApiDocMdx/ApiDocMdx.tsx @@ -1,10 +1,10 @@ import React, { useMemo } from 'react'; import Redoc from '@theme/Redoc'; import useSpecData from '@theme/useSpecData'; -import { MdxProps as Props } from '../../types/common'; +import { MdxProps } from '../../types/common'; import '../ApiSchema/styles.css'; -const ApiDocMdx: React.FC = ({ id }: Props): JSX.Element => { +const ApiDocMdx: React.FC = ({ id }: MdxProps): JSX.Element => { const specProps = useSpecData(id); const optionsOverrides = useMemo(() => { return { diff --git a/packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx b/packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx index 05882aa3..a278a4bf 100644 --- a/packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/ApiSchema/ApiSchema.tsx @@ -3,20 +3,25 @@ import clsx from 'clsx'; import { ThemeProvider } from 'styled-components'; import '../../global'; import { SchemaDefinition } from 'redoc'; -import { useSpec } from '../../utils/useSpec'; -import { useSpecData } from '../useSpecData'; -import { ApiSchemaProps as Props } from '../../types/common'; +import useSpec from '@theme/useSpec'; import '../Redoc/styles.css'; import './styles.css'; -const ApiSchema: React.FC = ({ - id, - example, +const ApiSchema: React.FC = ({ + showExample, pointer, + id, + spec, + optionsOverrides, ...rest -}: Props): JSX.Element => { - const specProps = useSpecData(id); - const { store } = useSpec(specProps); +}: ApiSchemaProps): JSX.Element => { + const { store } = useSpec( + { + id, + spec, + }, + optionsOverrides, + ); useEffect(() => { /** @@ -31,13 +36,14 @@ const ApiSchema: React.FC = ({ className={clsx([ 'redocusaurus', 'redocusaurus-schema', - example ? null : 'hide-example', + showExample ? null : 'hide-example', ])} > @@ -46,7 +52,7 @@ const ApiSchema: React.FC = ({ }; ApiSchema.defaultProps = { - example: false, + showExample: false, }; export default ApiSchema; diff --git a/packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx b/packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx index ba97a079..17bf7280 100644 --- a/packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/Redoc/Redoc.tsx @@ -1,29 +1,26 @@ import React from 'react'; import clsx from 'clsx'; import '../../global'; -import { RedocStandalone, RedocRawOptions } from 'redoc'; -import { SpecProps } from '../../types/common'; -import { useSpecOptions } from '../../utils/useSpecOptions'; +import { RedocStandalone } from 'redoc'; +import useSpecOptions from '@theme/useSpecOptions'; import './styles.css'; import ServerRedoc from './ServerRedoc'; +function getIsExternalUrl(url = '') { + return ['http://', 'https://'].some((protocol) => url.startsWith(protocol)); +} + /*! * Redocusaurus * https://redocusaurus.vercel.app/ * (c) 2024 Rohit Gohri * Released under the MIT License */ -function Redoc( - props: Partial & { - className?: string; - optionsOverrides?: RedocRawOptions; - }, -): JSX.Element { - const { className, optionsOverrides, spec, url, themeId, isSpecFile } = props; +function Redoc(props: RedocProps): JSX.Element { + const { className, optionsOverrides, url, themeId } = props; const { options } = useSpecOptions(themeId, optionsOverrides); - const isDevMode = process.env.NODE_ENV === 'development'; - if ((isDevMode && isSpecFile === false) || !spec) { + if (getIsExternalUrl(url)) { return (
@@ -31,7 +28,7 @@ function Redoc( ); } - return ; + return ; } export default Redoc; diff --git a/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerRedoc.tsx b/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerRedoc.tsx index ca940fd8..8d81ee0a 100644 --- a/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerRedoc.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerRedoc.tsx @@ -1,9 +1,8 @@ import React from 'react'; import clsx from 'clsx'; import '../../global'; -import { Redoc as RedocComponent, RedocRawOptions } from 'redoc'; -import { SpecProps } from '../../types/common'; -import { useSpec } from '../../utils/useSpec'; +import { Redoc as RedocComponent } from 'redoc'; +import useSpec from '@theme/useSpec'; import { ServerStyles } from './Styles'; import './styles.css'; @@ -13,22 +12,22 @@ import './styles.css'; * (c) 2024 Rohit Gohri * Released under the MIT License */ -function ServerRedoc( - props: SpecProps & { - className?: string; - optionsOverrides?: RedocRawOptions; - }, -): JSX.Element { - const { className, optionsOverrides, ...specProps } = props; - const { store, darkThemeOptions, lightThemeOptions, hasLogo } = useSpec( - specProps, +function ServerRedoc(props: RedocProps): JSX.Element { + const { className, optionsOverrides, url, id, themeId } = props; + const { store, spec, darkThemeOptions, lightThemeOptions, hasLogo } = useSpec( + { + spec: props.spec, + themeId, + id, + }, optionsOverrides, ); return ( <> diff --git a/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx b/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx index 1969cdd5..bf1a3da2 100644 --- a/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/Redoc/ServerStyles.tsx @@ -2,6 +2,7 @@ import React from 'react'; import '../../global'; import useBaseUrl from '@docusaurus/useBaseUrl'; import { AppStore, Redoc, RedocRawOptions } from 'redoc'; +import type { OpenAPISpec } from 'redoc/typings/types'; // eslint-disable-next-line import/no-extraneous-dependencies import { renderToString } from 'react-dom/server'; import { ServerStyleSheet } from 'styled-components'; @@ -53,22 +54,26 @@ const prefixCssSelectors = function (rules: string, className: string) { const LIGHT_MODE_PREFIX = "html:not([data-theme='dark'])"; const DARK_MODE_PREFIX = "html([data-theme='dark'])"; +export type ServerStylesProps = { + spec: OpenAPISpec; + url?: string; + lightThemeOptions: RedocRawOptions; + darkThemeOptions: RedocRawOptions; +}; + export function ServerStyles({ - specProps, + spec, + url, lightThemeOptions, darkThemeOptions, -}: { - specProps: SpecProps; - lightThemeOptions: RedocRawOptions; - darkThemeOptions: RedocRawOptions; -}) { - const fullUrl = useBaseUrl(specProps.url, { absolute: true }); +}: ServerStylesProps) { + const fullUrl = useBaseUrl(url, { absolute: true }); const css = { light: '', dark: '', }; const lightSheet = new ServerStyleSheet(); - const lightStore = new AppStore(specProps.spec, fullUrl, lightThemeOptions); + const lightStore = new AppStore(spec, fullUrl, lightThemeOptions); renderToString( lightSheet.collectStyles(React.createElement(Redoc, { store: lightStore })), ); @@ -78,7 +83,7 @@ export function ServerStyles({ css.light = prefixCssSelectors(lightCss, LIGHT_MODE_PREFIX); const darkSheet = new ServerStyleSheet(); - const darkStore = new AppStore(specProps.spec, fullUrl, darkThemeOptions); + const darkStore = new AppStore(spec, fullUrl, darkThemeOptions); renderToString( darkSheet.collectStyles(React.createElement(Redoc, { store: darkStore })), ); diff --git a/packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx b/packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx index 7a3bf976..17a39d0c 100644 --- a/packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx +++ b/packages/docusaurus-theme-redoc/src/theme/Redoc/Styles.tsx @@ -1,15 +1,11 @@ import React from 'react'; import '../../global'; -import type { RedocRawOptions } from 'redoc'; +import type { ServerStylesProps } from './ServerStyles'; /** * Don't hydrate/replace server styles * @see https://github.com/facebook/react/issues/10923#issuecomment-338715787 */ -export function ServerStyles(_props: { - specProps: SpecProps; - lightThemeOptions: RedocRawOptions; - darkThemeOptions: RedocRawOptions; -}) { +export function ServerStyles(_props: ServerStylesProps) { return
; } diff --git a/packages/docusaurus-theme-redoc/src/theme/useSpec/index.ts b/packages/docusaurus-theme-redoc/src/theme/useSpec/index.ts new file mode 100644 index 00000000..30c5c48c --- /dev/null +++ b/packages/docusaurus-theme-redoc/src/theme/useSpec/index.ts @@ -0,0 +1,3 @@ +import { useSpec } from './useSpec'; + +export default useSpec; diff --git a/packages/docusaurus-theme-redoc/src/utils/useSpec.ts b/packages/docusaurus-theme-redoc/src/theme/useSpec/useSpec.ts similarity index 82% rename from packages/docusaurus-theme-redoc/src/utils/useSpec.ts rename to packages/docusaurus-theme-redoc/src/theme/useSpec/useSpec.ts index fc10853e..a60f4e9d 100644 --- a/packages/docusaurus-theme-redoc/src/utils/useSpec.ts +++ b/packages/docusaurus-theme-redoc/src/theme/useSpec/useSpec.ts @@ -2,10 +2,10 @@ import { useMemo, useEffect } from 'react'; import useBaseUrl from '@docusaurus/useBaseUrl'; import useIsBrowser from '@docusaurus/useIsBrowser'; import { useColorMode } from '@docusaurus/theme-common'; -import '../global'; +import useSpecData from '@theme/useSpecData'; +import useSpecOptions from '@theme/useSpecOptions'; +import '../../global'; import { AppStore, RedocRawOptions } from 'redoc'; -import { SpecProps } from '../types/common'; -import { useSpecOptions } from './useSpecOptions'; // the current store singleton in the app's instance let currentStore: AppStore | null = null; @@ -17,9 +17,14 @@ let currentStore: AppStore | null = null; * Released under the MIT License */ export function useSpec( - { spec, url, themeId }: SpecProps, + specInfo: SpecProps, optionsOverrides?: RedocRawOptions, -) { +): SpecResult { + const { spec, url, themeId } = useSpecData( + specInfo.id, + specInfo.spec, + specInfo.themeId, + ); const specOptions = useSpecOptions(themeId, optionsOverrides); const fullUrl = useBaseUrl(url, { absolute: true }); const isBrowser = useIsBrowser(); @@ -36,6 +41,7 @@ export function useSpec( // @ts-expect-error extra prop hasLogo: !!spec.info?.['x-logo'], store: currentStore, + spec, }; }, [isBrowser, spec, fullUrl, specOptions]); diff --git a/packages/docusaurus-theme-redoc/src/theme/useSpecData.ts b/packages/docusaurus-theme-redoc/src/theme/useSpecData.ts deleted file mode 100644 index e8ecd96e..00000000 --- a/packages/docusaurus-theme-redoc/src/theme/useSpecData.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useAllPluginInstancesData } from '@docusaurus/useGlobalData'; -import { SpecProps } from '../types/common'; - -/** - * - * @param id ID of plugin data - * @returns Spec Data of ID or first one if ID is not provided - */ -export function useSpecData(id?: string): SpecProps { - const allData = useAllPluginInstancesData('docusaurus-plugin-redoc'); - const apiData = id - ? allData?.[id as string] - : Object.values(allData ?? {})?.[0]; - - return apiData as SpecProps; -} - -export default useSpecData; diff --git a/packages/docusaurus-theme-redoc/src/theme/useSpecData/index.ts b/packages/docusaurus-theme-redoc/src/theme/useSpecData/index.ts new file mode 100644 index 00000000..da2a5330 --- /dev/null +++ b/packages/docusaurus-theme-redoc/src/theme/useSpecData/index.ts @@ -0,0 +1,3 @@ +import { useSpecData } from './useSpecData'; + +export default useSpecData; diff --git a/packages/docusaurus-theme-redoc/src/theme/useSpecData/useSpecData.ts b/packages/docusaurus-theme-redoc/src/theme/useSpecData/useSpecData.ts new file mode 100644 index 00000000..a6294d48 --- /dev/null +++ b/packages/docusaurus-theme-redoc/src/theme/useSpecData/useSpecData.ts @@ -0,0 +1,35 @@ +import { useAllPluginInstancesData } from '@docusaurus/useGlobalData'; +import type { OpenAPISpec } from 'redoc/typings/types'; +import { SpecDataResult } from '../../types/common'; + +export type ParsedSpec = OpenAPISpec; + +/** + * Retrive the spec data to give to Redoc + * if providedSpec.spec is provided, use it + * otherwise use Spec Data using docusaurus config + * using providedSpec.id (or first one if not provided) + * + * @param providedSpec spec data + * @returns Spec Data of ID or first one if ID is not provided + */ +export function useSpecData( + id?: string, + spec?: OpenAPISpec, + themeId?: string, +): SpecDataResult { + const allData = useAllPluginInstancesData('docusaurus-plugin-redoc'); + if (spec) { + // return provided spec when already defined + return { + spec, + themeId, + }; + } else { + // retrieve spec from docusaurus conf + const apiData = id + ? allData?.[id as string] + : Object.values(allData ?? {})?.[0]; + return apiData as SpecDataResult; + } +} diff --git a/packages/docusaurus-theme-redoc/src/theme/useSpecOptions/index.ts b/packages/docusaurus-theme-redoc/src/theme/useSpecOptions/index.ts new file mode 100644 index 00000000..a7fdb6ed --- /dev/null +++ b/packages/docusaurus-theme-redoc/src/theme/useSpecOptions/index.ts @@ -0,0 +1,3 @@ +import { useSpecOptions } from './useSpecOptions'; + +export default useSpecOptions; diff --git a/packages/docusaurus-theme-redoc/src/utils/useSpecOptions.ts b/packages/docusaurus-theme-redoc/src/theme/useSpecOptions/useSpecOptions.ts similarity index 94% rename from packages/docusaurus-theme-redoc/src/utils/useSpecOptions.ts rename to packages/docusaurus-theme-redoc/src/theme/useSpecOptions/useSpecOptions.ts index a50c2e76..3a867167 100644 --- a/packages/docusaurus-theme-redoc/src/utils/useSpecOptions.ts +++ b/packages/docusaurus-theme-redoc/src/theme/useSpecOptions/useSpecOptions.ts @@ -6,10 +6,10 @@ import { } from '@docusaurus/useGlobalData'; import { useColorMode } from '@docusaurus/theme-common'; import merge from 'lodash/merge'; -import '../global'; +import '../../global'; import { RedocRawOptions } from 'redoc'; -import { SpecProps } from '../types/common'; -import { GlobalData } from '../types/options'; +import { SpecProps } from '../../types/common'; +import { GlobalData } from '../../types/options'; /** * Redocusaurus diff --git a/packages/docusaurus-theme-redoc/src/types/common.ts b/packages/docusaurus-theme-redoc/src/types/common.ts index db69f65a..03937907 100644 --- a/packages/docusaurus-theme-redoc/src/types/common.ts +++ b/packages/docusaurus-theme-redoc/src/types/common.ts @@ -1,17 +1,28 @@ import type { Props as LayoutProps } from '@theme/Layout'; import type { ObjectDescriptionProps } from 'redoc'; -import type { OpenAPISpec } from 'redoc/typings/types'; - -export type ParsedSpec = OpenAPISpec; export interface SpecProps { - spec: ParsedSpec; - url?: string; - isSpecFile?: boolean; + /** + * Spec to use, already loaded previously + */ + spec: import('redoc/typings/types').OpenAPISpec; + /** + * When spec not provided, load the spec from docusaurus config + * fallback to first configuration if not provided + */ + id?: string; + /** + * docusaurus theme to use + */ themeId?: string; } -export type RedocProps = SpecProps; +export type SpecDataResult = Omit & { + /** + * Public path to the spec file used, used by Redoc as download url + */ + url?: string; +}; export interface MdxProps { /** @@ -21,21 +32,6 @@ export interface MdxProps { id?: string; } -export type ApiSchemaProps = Omit< - ObjectDescriptionProps, - 'parser' | 'options' | 'schemaRef' -> & - MdxProps & { - /** - * Show the example or not - */ - example?: boolean; - /** - * Ref to the schema - */ - pointer: ObjectDescriptionProps['schemaRef']; - }; - export type ApiDocProps = { specProps: SpecProps; layoutProps?: Omit; diff --git a/packages/docusaurus-theme-redoc/src/types/modules.ts b/packages/docusaurus-theme-redoc/src/types/modules.ts index a4b38f75..7cf9b28d 100644 --- a/packages/docusaurus-theme-redoc/src/types/modules.ts +++ b/packages/docusaurus-theme-redoc/src/types/modules.ts @@ -1,19 +1,50 @@ interface SpecProps { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + /** + * Spec to use, already loaded previously + */ spec: import('redoc/typings/types').OpenAPISpec; - url?: string; - isSpecFile?: boolean; + /** + * When spec not provided, load the spec from docusaurus config + * fallback to first configuration if not provided + */ + id?: string; + /** + * docusaurus theme to use + */ themeId?: string; } +interface SpecResult { + hasLogo: boolean; + spec: import('redoc/typings/types').OpenAPISpec; + store: import('redoc/typings').AppStore; + options: import('redoc/typings').RedocRawOptions; + darkThemeOptions: import('redoc/typings').RedocRawOptions; + lightThemeOptions: import('redoc/typings').RedocRawOptions; +} + +type RedocProps = SpecProps & { + className?: string; + optionsOverrides?: import('redoc/typings').RedocRawOptions; + /** + * External URL to load spec file from + */ + url?: string; +}; + +type ApiSchemaProps = Omit< + import('redoc/typings').ObjectDescriptionProps, + 'parser' | 'options' | 'schemaRef' +> & + SpecProps & { + /** + * Ref to the schema + */ + pointer: import('redoc/typings').ObjectDescriptionProps['schemaRef']; + optionsOverrides?: import('redoc/typings').RedocRawOptions; + }; declare module '@theme/Redoc' { - import type { RedocRawOptions } from 'redoc'; - const Redoc: ( - props: SpecProps & { - className?: string; - optionsOverrides?: RedocRawOptions; - }, - ) => JSX.Element; + const Redoc: (props: RedocProps) => JSX.Element; export default Redoc; } @@ -49,31 +80,56 @@ declare module '@theme/ApiDocMdx' { } declare module '@theme/ApiSchema' { - interface ApiSchemaProps { - /** - * If you have multiple apis, then add a `id` field in the specs array - * And pass the same here - */ - id?: string; - /** - * Show the example or not - */ - example?: boolean; - - /** - * Ref to the schema - */ - pointer: string; - } - const ApiSchema: (props: ApiSchemaProps) => JSX.Element; export default ApiSchema; } declare module '@theme/useSpecData' { + type SpecDataResult = Omit & { + /** + * Public path to the spec file used, used by Redoc as download url + */ + url?: string; + }; + /** * Load redocusaurus plugin data by ID */ - const useSpecData: (id?: string) => SpecProps; + const useSpecData: ( + id?: string, + spec?: import('redoc/typings/types').OpenAPISpec, + themeId?: string, + ) => SpecDataResult; export default useSpecData; } + +declare module '@theme/useSpec' { + import { RedocRawOptions } from 'redoc'; + /** + * Load redocusaurus plugin data by ID + */ + const useSpec: ( + specInfo: SpecProps, + optionsOverrides?: RedocRawOptions, + ) => SpecResult; + + export default useSpec; +} + +declare module '@theme/useSpecOptions' { + import { RedocRawOptions } from 'redoc'; + interface SpecOptionsResultProps { + options: RedocRawOptions; + darkThemeOptions: RedocRawOptions; + lightThemeOptions: RedocRawOptions; + } + /** + * Load redocusaurus plugin data by ID + */ + const useSpec: ( + themeId: SpecProps['themeId'], + optionsOverrides?: RedocRawOptions, + ) => SpecOptionsResultProps; + + export default useSpec; +} diff --git a/website/docs/getting-started/Installation.md b/website/docs/getting-started/Installation.md index d23d7e57..85ae546e 100644 --- a/website/docs/getting-started/Installation.md +++ b/website/docs/getting-started/Installation.md @@ -72,7 +72,7 @@ author_url: https://rohit.page ``` The API Doc will be available at the path specific by `route`. To skip adding a route altogether just don't set the `route` property. -You will still be able to reference schema elements manually using [Schema Imports](/docs/guides/schema-imports) or create Custom React Pages using the data and theme components. +You will still be able to reference schema elements manually using [Schema Imports](/docs/guides/component-api-schema) or create Custom React Pages using the data and theme components. If you have a [`redocly.yaml`](https://redocly.com/docs/cli/configuration/) it will be loaded automatically. ## Options diff --git a/website/docs/guides/build-time-rendering.md b/website/docs/guides/build-time-rendering.md index 8dff201a..e6516dba 100644 --- a/website/docs/guides/build-time-rendering.md +++ b/website/docs/guides/build-time-rendering.md @@ -1,7 +1,7 @@ --- title: Build Time Rendering description: Parse the OpenAPI schema at build time and skip the loading screen -sidebar_position: 3 +sidebar_position: 4 --- :::warning diff --git a/website/docs/guides/component-api-schema.mdx b/website/docs/guides/component-api-schema.mdx new file mode 100644 index 00000000..d6b28325 --- /dev/null +++ b/website/docs/guides/component-api-schema.mdx @@ -0,0 +1,125 @@ +--- +title: Component ApiSchema +sidebar_position: 2 +--- + +import ApiSchema from '@theme/ApiSchema'; +import openApi from '../../openapi/using-single-json.openapi.json' + +# ApiSchema + +You can display model definitions from your API schema and render them in your Docusaurus Docs. You'll need to create an `.mdx` file and import the React Component. Read more [here about MDX in Docusaurus](https://docusaurus.io/docs/markdown-features/react). + +:::info File format +You cannot import a React component inside a `.md` file. +Change your file extension to `.mdx` before importing the React Component. +Read more [here about MDX in Docusaurus](https://docusaurus.io/docs/markdown-features/react). +::: + +## Import + +```tsx +import ApiSchema from '@theme/ApiSchema'; +``` + +## Props + +| Name | Type | Description | +|-------------|--------------|------------------------------------------------------------------------------------------------------| +| pointer | String | the redoc reference to display in [Redoc](https://redoc.ly/docs/resources/ref-guide/#pointer) format | +| showExample | boolean | (default: false) allow to display example | +| id | String | When spec not provided, load the spec from docusaurus config. Use first spec if not defined | +| spec | OpenAPI spec | A JSON content spec to use | +| themeId | String | redocusaurus theme to use - default to `theme-redoc` | + +## Examples + + +### Basic example + +The `pointer` prop is passed on to [Redoc](https://redoc.ly/docs/resources/ref-guide/#pointer). +It displays here the first element of the redocusaurus configuration. + +```tsx +import ApiSchema from '@theme/ApiSchema'; + + +``` + + + +### Display example + +```tsx +import ApiSchema from '@theme/ApiSchema'; + + +``` + + + +### Multiple OpenAPI schemas example + +If you have multiple APIs loaded with redocusaurus, then it is recommended to add `id`s to the config so that you can refer them when loading schema models. + +```js title="docusaurus.config.js" +const config = { + presets: [ + '@docusaurus/preset-classic', + [ + 'redocusaurus', + { + openapi: { + path: 'openapi', + routeBasePath: '/examples', + }, + specs: [ + { + id: 'using-remote-url', + spec: 'https://redocly.github.io/redoc/openapi.yaml', + route: '/examples/using-remote-url/', + }, + ], + [...] + }, + ], + ], +}; +``` + +```tsx +import ApiSchema from '@theme/ApiSchema'; + + + +``` + +#### Results for ID `id="using-single-yaml"` + + + +#### Results for ID `id="using-remote-url"` + + + +### Webpack loader example + +You can provide a JSON spec to the component like this. Webpack will load the file directly, +you don't need to use redocusaurus configuration inside `docusaurus.config.js`. + +```tsx +import ApiSchema from '@theme/ApiSchema'; +import openApi from './api-with-examples.json' + + +``` + + + +:::info YAML support +You cannot load yaml file like this: +```tsx +import openApi from './api-with-examples.yaml' +``` +Without the right webpack configuration to handle such file format. +::: diff --git a/website/docs/guides/component-redoc.mdx b/website/docs/guides/component-redoc.mdx new file mode 100644 index 00000000..54072ee4 --- /dev/null +++ b/website/docs/guides/component-redoc.mdx @@ -0,0 +1,65 @@ +--- +title: Component Redoc +sidebar_position: 1 +--- + +import Redoc from '@theme/Redoc'; +import openApi from '../../openapi/using-single-json.openapi.json' + +# Redoc + +You can display the whole OpenAPI documentation using a React component + +:::info File format +You cannot import a React component inside a `.md` file. +Change your file extension to `.mdx` before importing the React Component. +Read more [here about MDX in Docusaurus](https://docusaurus.io/docs/markdown-features/react). +::: + +## Import + +```tsx +import Redoc from '@theme/Redoc'; +``` + +## Props + +| Name | Type | Description | +|------|--------------|-------------------------------------| +| spec | OpenAPI spec | A JSON content spec to use | +| url | String | External URL to load spec file from | + +## Examples + +### External URL example + +```tsx +import Redoc from '@theme/Redoc'; + + +``` + + + +### Webpack loader example + +You can provide a JSON spec to the component like this. Webpack will load the file directly, +you don't need to use redocusaurus configuration inside `docusaurus.config.js`. + +```tsx +import Redoc from '@theme/Redoc'; +import openApi from './api-with-examples.json' + + +``` + + + +:::info YAML support +You cannot load yaml file like this: +```tsx +import openApi from './api-with-examples.yaml' +``` +Without the right webpack configuration to handle such file format. +::: + diff --git a/website/docs/guides/migrating-to-v1.md b/website/docs/guides/migrating-to-v1.md index d3380fc5..68046436 100644 --- a/website/docs/guides/migrating-to-v1.md +++ b/website/docs/guides/migrating-to-v1.md @@ -1,6 +1,6 @@ --- title: Migrating to V1 -sidebar_position: 4 +sidebar_position: 5 --- ## Options Changed diff --git a/website/docs/guides/multiple-apis.md b/website/docs/guides/multiple-apis.md index b872f571..29e3224e 100644 --- a/website/docs/guides/multiple-apis.md +++ b/website/docs/guides/multiple-apis.md @@ -1,6 +1,6 @@ --- title: Showing Multiple APIs -sidebar_position: 2 +sidebar_position: 3 --- ## Nested View with MDX diff --git a/website/docs/guides/schema-imports.mdx b/website/docs/guides/schema-imports.mdx deleted file mode 100644 index 544707dc..00000000 --- a/website/docs/guides/schema-imports.mdx +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Schema Imports -sidebar_position: 1 ---- - -import ApiSchema from '@theme/ApiSchema'; - -# Schema Imports - -You can import model definitions from your API schema and render them in your Docusaurus Docs. You'll need to create an `.mdx` file and import the React Component. Read more [here about MDX in Docusaurus](https://docusaurus.io/docs/markdown-features/react). - -# Import Schema Model in Docs - -The `pointer` prop is passed on to [Redoc](https://redoc.ly/docs/resources/ref-guide/#pointer). - -```tsx -import ApiSchema from '@theme/ApiSchema'; - -; -``` - -### Results - - - -## Import Schema Model (with example) in Docs - -```tsx -import ApiSchema from '@theme/ApiSchema'; - -; -``` - -### Results - - - -## Importing Schema Model with multiple OpenAPI schemas - -If you have multiple APIs loaded with redocusaurus, then it is recommended to add `id`s to the config so that you can refer them when loading schema models. - -```js title="docusaurus.config.js" -const config = { - presets: [ - '@docusaurus/preset-classic', - [ - 'redocusaurus', - { - specs: [ - { - id: 'using-single-yaml', - spec: 'openapi/single-file/openapi.yaml', - route: '/examples/using-single-yaml/', - }, - { - id: 'using-remote-url', - spec: 'https://redocly.github.io/redoc/openapi.yaml', - route: '/examples/using-remote-url/', - }, - ], - theme: { - /** - * Highlight color for docs - */ - primaryColor: '#1890ff', - /** - * Options to pass to redoc - * @see https://github.com/redocly/redoc#redoc-options-object - */ - options: { disableSearch: true }, - }, - }, - ], - ], - title: 'Redocusaurus', -}; -``` - -```tsx -import ApiSchema from '@theme/ApiSchema'; - -; -; -``` - -### Results - -#### For ID `id="using-single-yaml"` - - - -#### For ID `id="using-remote-url"` - - diff --git a/website/openapi/using-single-json.openapi.json b/website/openapi/using-single-json.openapi.json new file mode 100644 index 00000000..4e83fd3a --- /dev/null +++ b/website/openapi/using-single-json.openapi.json @@ -0,0 +1,1720 @@ +{ + "openapi": "3.0.0", + "servers": [ + { + "url": "//petstore.swagger.io/v2", + "description": "Default server" + }, + { + "url": "//petstore.swagger.io/sandbox", + "description": "Sandbox server" + } + ], + "info": { + "description": "This is a sample server Petstore server.\nYou can find out more about Swagger at\n[http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/).\nFor this sample, you can use the api key `special-key` to test the authorization filters.\n\n# Introduction\nThis API is documented in **OpenAPI format** and is based on\n[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.\nIt was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)\ntool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard\nOpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).\n\n# OpenAPI Specification\nThis API is documented in **OpenAPI format** and is based on\n[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.\nIt was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)\ntool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard\nOpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).\n\n# Cross-Origin Resource Sharing\nThis API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).\nAnd that allows cross-domain communication from the browser.\nAll responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.\n\n# Authentication\n\nPetstore offers two forms of authentication:\n - API Key\n - OAuth2\nOAuth2 - an open protocol to allow secure authorization in a simple\nand standard method from web, mobile and desktop applications.\n\n\n", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "API Support", + "email": "apiteam@swagger.io", + "url": "https://github.com/Redocly/redoc" + }, + "x-logo": { + "url": "https://redocly.github.io/redoc/petstore-logo.png", + "altText": "Petstore logo" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "externalDocs": { + "description": "Find out how to create Github repo for your OpenAPI spec.", + "url": "https://github.com/Rebilly/generator-openapi-repo" + }, + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets" + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user" + }, + { + "name": "pet_model", + "x-displayName": "The Pet Model", + "description": "\n" + }, + { + "name": "store_model", + "x-displayName": "The Order Model", + "description": "\n" + } + ], + "x-tagGroups": [ + { + "name": "General", + "tags": [ + "pet", + "store" + ] + }, + { + "name": "User Management", + "tags": [ + "user" + ] + }, + { + "name": "Models", + "tags": [ + "pet_model", + "store_model" + ] + } + ], + "security": [ + {} + ], + "paths": { + "/pet": { + "parameters": [ + { + "name": "Accept-Language", + "in": "header", + "description": "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US", + "example": "en-US", + "required": false, + "schema": { + "type": "string", + "default": "en-AU" + } + }, + { + "name": "cookieParam", + "in": "cookie", + "description": "Some cookie", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "Add new pet to the store inventory.", + "operationId": "addPet", + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "x-codeSamples": [ + { + "lang": "C#", + "source": "PetStore.v1.Pet pet = new PetStore.v1.Pet();\npet.setApiKey(\"your api key\");\npet.petType = PetStore.v1.Pet.TYPE_DOG;\npet.name = \"Rex\";\n// set other fields\nPetStoreResponse response = pet.create();\nif (response.statusCode == HttpStatusCode.Created)\n{\n // Successfully created\n}\nelse\n{\n // Something wrong -- check response for errors\n Console.WriteLine(response.getRawResponse());\n}\n" + }, + { + "lang": "PHP", + "source": "$form = new \\PetStore\\Entities\\Pet();\n$form->setPetType(\"Dog\");\n$form->setName(\"Rex\");\n// set other fields\ntry {\n $pet = $client->pets()->create($form);\n} catch (UnprocessableEntityException $e) {\n var_dump($e->getErrors());\n}\n" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "x-codeSamples": [ + { + "lang": "PHP", + "source": "$form = new \\PetStore\\Entities\\Pet();\n$form->setPetId(1);\n$form->setPetType(\"Dog\");\n$form->setName(\"Rex\");\n// set other fields\ntry {\n $pet = $client->pets()->update($form);\n} catch (UnprocessableEntityException $e) {\n var_dump($e->getErrors());\n}\n" + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/Pet" + } + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "deprecated": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "name": { + "description": "Updated name of the pet", + "type": "string" + }, + "status": { + "description": "Updated status of the pet", + "type": "string" + } + } + } + } + } + } + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "schema": { + "type": "string" + }, + "example": "Bearer " + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "style": "form", + "schema": { + "type": "array", + "minItems": 1, + "maxItems": 3, + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "deprecated": true, + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "style": "form", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/xml": { + "schema": { + "type": "array", + "maxItems": 999, + "items": { + "maxItems": 111, + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "minProperties": 2, + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid Order", + "content": { + "application/json": { + "example": { + "status": 400, + "message": "Invalid Order" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "description": "order placed for purchasing the pet", + "required": true + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 1, + "maximum": 5 + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "string", + "minimum": 1 + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/store/subscribe": { + "post": { + "tags": [ + "store" + ], + "summary": "Subscribe to the Store events", + "description": "Add subscription for a store events", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "callbackUrl": { + "type": "string", + "format": "uri", + "description": "This URL will be called by the server when the desired event will occur", + "example": "https://myserver.com/send/callback/here" + }, + "eventName": { + "type": "string", + "description": "Event name for the subscription", + "enum": [ + "orderInProgress", + "orderShipped", + "orderDelivered" + ], + "example": "orderInProgress" + } + }, + "required": [ + "callbackUrl", + "eventName" + ] + } + } + } + }, + "responses": { + "201": { + "description": "Subscription added", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "subscriptionId": { + "type": "string", + "example": "AAA-123-BBB-456" + } + } + } + } + } + } + }, + "callbacks": { + "orderInProgress": { + "{$request.body#/callbackUrl}?event={$request.body#/eventName}": { + "servers": [ + { + "url": "//callback-url.path-level/v1", + "description": "Path level server 1" + }, + { + "url": "//callback-url.path-level/v2", + "description": "Path level server 2" + } + ], + "post": { + "summary": "Order in Progress (Summary)", + "description": "A callback triggered every time an Order is updated status to \"inProgress\" (Description)", + "externalDocs": { + "description": "Find out more", + "url": "https://more-details.com/demo" + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2018-10-19T16:46:45Z" + }, + "status": { + "type": "string", + "example": "inProgress" + } + } + } + }, + "application/xml": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + } + } + }, + "example": "\n\n 123\n inProgress\n 2018-10-19T16:46:45Z\n\n" + } + } + }, + "responses": { + "200": { + "description": "Callback successfully processed and no retries will be performed", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "someProp": { + "type": "string", + "example": "123" + } + } + } + } + } + }, + "299": { + "description": "Response for cancelling subscription" + }, + "500": { + "description": "Callback processing failed and retries will be performed" + } + }, + "x-codeSamples": [ + { + "lang": "C#", + "source": "PetStore.v1.Pet pet = new PetStore.v1.Pet();\npet.setApiKey(\"your api key\");\npet.petType = PetStore.v1.Pet.TYPE_DOG;\npet.name = \"Rex\";\n// set other fields\nPetStoreResponse response = pet.create();\nif (response.statusCode == HttpStatusCode.Created)\n{\n // Successfully created\n}\nelse\n{\n // Something wrong -- check response for errors\n Console.WriteLine(response.getRawResponse());\n}\n" + }, + { + "lang": "PHP", + "source": "$form = new \\PetStore\\Entities\\Pet();\n$form->setPetType(\"Dog\");\n$form->setName(\"Rex\");\n// set other fields\ntry {\n $pet = $client->pets()->create($form);\n} catch (UnprocessableEntityException $e) {\n var_dump($e->getErrors());\n}\n" + } + ] + }, + "put": { + "description": "Order in Progress (Only Description)", + "servers": [ + { + "url": "//callback-url.operation-level/v1", + "description": "Operation level server 1 (Operation override)" + }, + { + "url": "//callback-url.operation-level/v2", + "description": "Operation level server 2 (Operation override)" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2018-10-19T16:46:45Z" + }, + "status": { + "type": "string", + "example": "inProgress" + } + } + } + }, + "application/xml": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + } + } + }, + "example": "\n\n 123\n inProgress\n 2018-10-19T16:46:45Z\n\n" + } + } + }, + "responses": { + "200": { + "description": "Callback successfully processed and no retries will be performed", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "someProp": { + "type": "string", + "example": "123" + } + } + } + } + } + } + } + } + } + }, + "orderShipped": { + "{$request.body#/callbackUrl}?event={$request.body#/eventName}": { + "post": { + "description": "Very long description\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor\nincididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis\nnostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu\nfugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in\nculpa qui officia deserunt mollit anim id est laborum.\n", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2018-10-19T16:46:45Z" + }, + "estimatedDeliveryDate": { + "type": "string", + "format": "date-time", + "example": "2018-11-11T16:00:00Z" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Callback successfully processed and no retries will be performed" + } + } + } + } + }, + "orderDelivered": { + "http://notificationServer.com?url={$request.body#/callbackUrl}&event={$request.body#/eventName}": { + "post": { + "deprecated": true, + "summary": "Order delivered", + "description": "A callback triggered every time an Order is delivered to the recipient", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "orderId": { + "type": "string", + "example": "123" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "example": "2018-10-19T16:46:45Z" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Callback successfully processed and no retries will be performed" + } + } + } + } + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Created user object", + "required": true + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "Updated user object", + "required": true + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/UserArray" + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "responses": { + "default": { + "description": "successful operation" + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/UserArray" + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "string" + }, + "examples": { + "response": { + "value": "OK" + } + } + }, + "application/xml": { + "schema": { + "type": "string" + }, + "examples": { + "response": { + "value": " OK " + } + } + }, + "text/plain": { + "examples": { + "response": { + "value": "OK" + } + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "responses": { + "default": { + "description": "successful operation" + } + } + } + } + }, + "components": { + "schemas": { + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "Cat": { + "x-tags": [ + "pet" + ], + "description": "A representation of a cat", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "huntingSkill": { + "type": "string", + "description": "The measured skill for hunting", + "default": "lazy", + "example": "adventurous", + "enum": [ + "clueless", + "lazy", + "adventurous", + "aggressive" + ] + } + }, + "required": [ + "huntingSkill" + ] + } + ] + }, + "Category": { + "type": "object", + "properties": { + "id": { + "description": "Category ID", + "allOf": [ + { + "$ref": "#/components/schemas/Id" + } + ] + }, + "name": { + "description": "Category name", + "type": "string", + "minLength": 1 + }, + "sub": { + "description": "Test Sub Category", + "type": "object", + "properties": { + "prop1": { + "type": "string", + "description": "Dumb Property" + } + } + } + }, + "xml": { + "name": "Category" + } + }, + "Dog": { + "description": "A representation of a dog", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "packSize": { + "type": "integer", + "format": "int32", + "description": "The size of the pack the dog is from", + "default": 1, + "minimum": 1 + } + }, + "required": [ + "packSize" + ] + } + ] + }, + "HoneyBee": { + "description": "A representation of a honey bee", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "honeyPerDay": { + "type": "number", + "description": "Average amount of honey produced per day in ounces", + "example": 3.14, + "multipleOf": 0.01 + } + }, + "required": [ + "honeyPerDay" + ] + } + ] + }, + "Id": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "Order": { + "type": "object", + "properties": { + "id": { + "description": "Order ID", + "allOf": [ + { + "$ref": "#/components/schemas/Id" + } + ] + }, + "petId": { + "description": "Pet ID", + "allOf": [ + { + "$ref": "#/components/schemas/Id" + } + ] + }, + "quantity": { + "type": "integer", + "format": "int32", + "minimum": 1, + "default": 1 + }, + "shipDate": { + "description": "Estimated ship date", + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "description": "Indicates whenever order was completed or not", + "type": "boolean", + "default": false, + "readOnly": true + }, + "requestId": { + "description": "Unique Request Id", + "type": "string", + "writeOnly": true + } + }, + "xml": { + "name": "Order" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "discriminator": { + "propertyName": "petType", + "mapping": { + "cat": "#/components/schemas/Cat", + "dog": "#/components/schemas/Dog", + "bee": "#/components/schemas/HoneyBee" + } + }, + "properties": { + "id": { + "externalDocs": { + "description": "Find more info here", + "url": "https://example.com" + }, + "description": "Pet ID", + "allOf": [ + { + "$ref": "#/components/schemas/Id" + } + ] + }, + "category": { + "description": "Categories this pet belongs to", + "allOf": [ + { + "$ref": "#/components/schemas/Category" + } + ] + }, + "name": { + "description": "The name given to a pet", + "type": "string", + "example": "Guru" + }, + "photoUrls": { + "description": "The list of URL to a cute photos featuring pet", + "type": "array", + "default": [], + "maxItems": 20, + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string", + "format": "url" + } + }, + "friend": { + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + } + ] + }, + "tags": { + "description": "Tags attached to the pet", + "type": "array", + "minItems": 1, + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "Pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + }, + "petType": { + "description": "Type of a pet", + "type": "string" + } + }, + "xml": { + "name": "Pet" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "description": "Tag ID", + "allOf": [ + { + "$ref": "#/components/schemas/Id" + } + ] + }, + "name": { + "description": "Tag name", + "type": "string", + "minLength": 1 + } + }, + "xml": { + "name": "Tag" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Id" + }, + "pet": { + "oneOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "$ref": "#/components/schemas/Tag" + } + ] + }, + "username": { + "description": "User supplied username", + "type": "string", + "minLength": 4, + "example": "John78" + }, + "firstName": { + "description": "User first name", + "type": "string", + "minLength": 1, + "example": "John" + }, + "lastName": { + "description": "User last name", + "type": "string", + "minLength": 1, + "example": "Smith" + }, + "email": { + "description": "User email address", + "type": "string", + "format": "email", + "example": "john.smith@example.com" + }, + "password": { + "type": "string", + "description": "User password, MUST contain a mix of upper and lower case letters, as well as digits", + "format": "password", + "minLength": 8, + "pattern": "/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/", + "example": "drowssaP123" + }, + "phone": { + "description": "User phone number in international format", + "type": "string", + "pattern": "/^\\+(?:[0-9]-?){6,14}[0-9]$/", + "example": "+1-202-555-0192" + }, + "userStatus": { + "description": "User status", + "type": "integer", + "format": "int32" + }, + "addresses": { + "type": "array", + "minItems": 0, + "maxLength": 10, + "items": [ + { + "type": "object", + "properties": { + "city": { + "type": "string", + "minLength": 0 + }, + "country": { + "type": "string", + "minLength": 0 + }, + "street": { + "description": "includes build/apartment number", + "type": "string", + "minLength": 0 + } + } + }, + { + "type": "number" + } + ], + "additionalItems": { + "type": "string" + } + } + }, + "xml": { + "name": "User" + } + } + }, + "requestBodies": { + "Pet": { + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "description": "My Pet", + "title": "Pettie" + }, + { + "$ref": "#/components/schemas/Pet" + } + ] + } + }, + "application/xml": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "hooray", + "default": [] + } + } + } + } + }, + "description": "Pet object that needs to be added to the store", + "required": true + }, + "UserArray": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "description": "List of user object", + "required": true + } + }, + "securitySchemes": { + "petstore_auth": { + "description": "Get access to data while protecting your account credentials.\nOAuth2 is also a safer and more secure way to give you access.\n", + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "description": "For this sample, you can use the api key `special-key` to test the authorization filters.\n", + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "examples": { + "Order": { + "value": { + "quantity": 1, + "shipDate": "2018-10-19T16:46:45Z", + "status": "placed", + "complete": false + } + } + } + }, + "x-webhooks": { + "newPet": { + "post": { + "summary": "New pet", + "description": "Information about a new pet in the systems", + "operationId": "newPet", + "tags": [ + "pet" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "responses": { + "200": { + "description": "Return a 200 status to indicate that the data was received successfully" + } + } + } + } + } +}