diff --git a/demo/openapi.yaml b/demo/openapi.yaml index 790a76c7..4dc5f229 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -112,6 +112,9 @@ paths: summary: Add a new pet to the store description: Add new pet to the store inventory. operationId: addPet + x-badges: + - name: Global + type: global responses: '405': description: Invalid input @@ -156,6 +159,9 @@ paths: summary: Update an existing pet description: '' operationId: updatePet + x-badges: + - name: US Only + type: usonly responses: '400': description: Invalid ID supplied @@ -186,6 +192,9 @@ paths: get: description: Retrieve the history information associated with an order. operationId: GetOrderHistory + x-badges: + - name: Success + type: success parameters: - description: '' in: path diff --git a/src/components/Operation/Operation.tsx b/src/components/Operation/Operation.tsx index e135c31e..2f743597 100644 --- a/src/components/Operation/Operation.tsx +++ b/src/components/Operation/Operation.tsx @@ -28,7 +28,15 @@ export interface OperationProps { } export const Operation = observer(({ operation }: OperationProps): JSX.Element => { - const { name: summary, description, deprecated, externalDocs, isWebhook, httpVerb } = operation; + const { + name: summary, + description, + deprecated, + badges, + externalDocs, + isWebhook, + httpVerb, + } = operation; const hasDescription = !!(description || externalDocs); const { showWebhookVerb } = React.useContext(OptionsContext); return ( @@ -45,6 +53,16 @@ export const Operation = observer(({ operation }: OperationProps): JSX.Element = Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()} )} + {badges.map(badge => { + return ( + badge && ( + + {' '} + {badge.name}{' '} + + ) + ); + })} {options.pathInMiddlePanel && !isWebhook && ( diff --git a/src/services/models/Operation.ts b/src/services/models/Operation.ts index e26b4a4c..96fbb43a 100644 --- a/src/services/models/Operation.ts +++ b/src/services/models/Operation.ts @@ -20,7 +20,12 @@ import { RequestBodyModel } from './RequestBody'; import { ResponseModel } from './Response'; import { SideNavStyleEnum } from '../types'; -import type { OpenAPIExternalDocumentation, OpenAPIServer, OpenAPIXCodeSample } from '../../types'; +import type { + OpenAPIExternalDocumentation, + OpenAPIServer, + OpenAPIXCodeSample, + OperationCustomBadge, +} from '../../types'; import type { OpenAPIParser } from '../OpenAPIParser'; import type { RedocNormalizedOptions } from '../RedocNormalizedOptions'; import type { MediaContentModel } from './MediaContent'; @@ -72,6 +77,7 @@ export class OperationModel implements IMenuItem { operationHash?: string; httpVerb: string; deprecated: boolean; + badges: OperationCustomBadge[]; path: string; servers: OpenAPIServer[]; security: SecurityRequirementModel[]; @@ -96,6 +102,7 @@ export class OperationModel implements IMenuItem { this.externalDocs = operationSpec.externalDocs; this.deprecated = !!operationSpec.deprecated; + this.badges = operationSpec['x-badges'] ? operationSpec['x-badges'] : []; this.httpVerb = operationSpec.httpVerb; this.deprecated = !!operationSpec.deprecated; this.operationId = operationSpec.operationId; diff --git a/src/theme.ts b/src/theme.ts index e11209b6..4fca8a65 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -37,6 +37,24 @@ const defaultTheme: ThemeInterface = { dark: ({ colors }) => darken(colors.tonalOffset, colors.error.main), contrastText: ({ colors }) => readableColor(colors.error.main), }, + global: { + main: '#7c1cfc', + light: ({ colors }) => lighten(colors.tonalOffset, colors.global.main), + dark: ({ colors }) => darken(colors.tonalOffset, colors.global.main), + contrastText: ({ colors }) => readableColor(colors.global.main), + }, + usonly: { + main: '#079cee', + light: ({ colors }) => lighten(colors.tonalOffset, colors.usonly.main), + dark: ({ colors }) => darken(colors.tonalOffset, colors.usonly.main), + contrastText: ({ colors }) => readableColor(colors.global.main), + }, + experimental: { + main: '#8c03fc', + light: ({ colors }) => lighten(colors.tonalOffset, colors.experimental.main), + dark: ({ colors }) => darken(colors.tonalOffset, colors.experimental.main), + contrastText: ({ colors }) => readableColor(colors.experimental.main), + }, gray: { 50: '#FAFAFA', 100: '#F5F5F5', @@ -267,6 +285,9 @@ export interface ResolvedThemeInterface { success: ColorSetting; warning: ColorSetting; error: ColorSetting; + global: ColorSetting; + usonly: ColorSetting; + experimental: ColorSetting; gray: { 50: string; 100: string; diff --git a/src/types/open-api.ts b/src/types/open-api.ts index fd80bf8d..dab3729c 100644 --- a/src/types/open-api.ts +++ b/src/types/open-api.ts @@ -64,6 +64,20 @@ export interface OpenAPIPath { $ref?: string; } +export type OperationCustomBadgeType = + | 'primary' + | 'success' + | 'warning' + | 'error' + | 'global' + | 'usonly' + | 'experimental'; + +export interface OperationCustomBadge { + name: string; + type: OperationCustomBadgeType; +} + export interface OpenAPIXCodeSample { lang: string; label?: string; @@ -83,6 +97,7 @@ export interface OpenAPIOperation { deprecated?: boolean; security?: OpenAPISecurityRequirement[]; servers?: OpenAPIServer[]; + 'x-badges'?: OperationCustomBadge[]; 'x-codeSamples'?: OpenAPIXCodeSample[]; 'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated } diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 22424e90..561811c7 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -662,6 +662,7 @@ export function isRedocExtension(key: string): boolean { 'x-traitTag': true, 'x-additionalPropertiesName': true, 'x-explicitMappingOnly': true, + 'x-badges': true, }; return key in redocExtensions;