Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support x-badges #2605

Merged
merged 4 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Redoc is provided as a CLI tool (also distributed as a Docker image), HTML tag,
If you have Node installed, quickly generate documentation using `npx`:

```bash
npx @redocly/cli build-docs openapi.yaml
npx @redocly/cli build-docs openapi.yaml
```

The tool outputs by default to a file named `redoc-static.html` that you can open in your browser.
Expand Down Expand Up @@ -116,6 +116,7 @@ Redoc uses the following [specification extensions](https://redocly.com/docs/api
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for tags that refer to non-navigation properties like Pagination, Rate-Limits, etc
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
* [`x-badges`](docs/redoc-vendor-extensions.md#x-badges) - specify operation badges
* [`x-examples`](docs/redoc-vendor-extensions.md#x-examples) - specify JSON example for requests
* [`x-nullable`](docs/redoc-vendor-extensions.md#x-nullable) - mark schema param as a nullable
* [`x-displayName`](docs/redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories
Expand Down
9 changes: 9 additions & 0 deletions demo/museum.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ paths:
operationId: getMuseumHours
tags:
- Operations
x-badges:
- name: 'Beta'
position: before
color: purple
parameters:
- $ref: '#/components/parameters/StartDate'
- $ref: '#/components/parameters/PaginationPage'
Expand Down Expand Up @@ -64,6 +68,9 @@ paths:
summary: Create special event
tags:
- Events
x-badges:
- name: 'Alpha'
color: purple
requestBody:
required: true
content:
Expand Down Expand Up @@ -92,6 +99,8 @@ paths:
description: Return a list of upcoming special events at the museum.
security: []
operationId: listSpecialEvents
x-badges:
- name: 'Gamma'
tags:
- Events
parameters:
Expand Down
9 changes: 9 additions & 0 deletions demo/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ paths:
post:
tags:
- pet
x-badges:
- name: 'Beta'
position: before
color: purple
summary: Add a new pet to the store
description: Add new pet to the store inventory.
operationId: addPet
Expand Down Expand Up @@ -150,6 +154,9 @@ paths:
put:
tags:
- pet
x-badges:
- name: 'Alpha'
color: purple
summary: Update an existing pet
description: ''
operationId: updatePet
Expand Down Expand Up @@ -183,6 +190,8 @@ paths:
get:
tags:
- pet
x-badges:
- name: 'Gamma'
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
Expand Down
5 changes: 5 additions & 0 deletions docs/redoc-vendor-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
Extends the OpenAPI root [OpenAPI Object](https://redocly.com/docs/openapi-visual-reference/openapi)

### x-servers
Backported from OpenAPI 3.0 [`servers`](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#serverObject). Currently doesn't support templates.

Check warning on line 56 in docs/redoc-vendor-extensions.md

View workflow job for this annotation

GitHub Actions / linkcheck

link checker warning


### x-tagGroups

Expand Down Expand Up @@ -215,7 +215,7 @@
| x-displayName | string | Defines the text that is used for this tag in the menu and in section headings |

## Operation Object vendor extensions
Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-reference/operation/)

Check warning on line 218 in docs/redoc-vendor-extensions.md

View workflow job for this annotation

GitHub Actions / linkcheck

link checker warning

https://redocly.com/docs/openapi-visual-reference/operation/. 200 - OK

### x-codeSamples
| Field Name | Type | Description |
Expand All @@ -233,7 +233,7 @@
#### Fixed fields
| Field Name | Type | Description |
| :---------- | :------: | :----------- |
| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) |

Check warning on line 236 in docs/redoc-vendor-extensions.md

View workflow job for this annotation

GitHub Actions / linkcheck

link checker warning

| label | string? | Code sample label, for example `Node` or `Python2.7`, _optional_, `lang` is used by default |
| source | string | Code sample source code |

Expand All @@ -252,13 +252,18 @@
source: console.log('Hello World');
```

### x-badges
| Field Name | Type | Description |
| :------------- | :------: | :---------- |
| x-badges | [[Badge Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-badges#badge-object)] | A list of badges associated with the operation |

## Parameter Object
Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/)

Check warning on line 261 in docs/redoc-vendor-extensions.md

View workflow job for this annotation

GitHub Actions / linkcheck

link checker warning

https://redocly.com/docs/openapi-visual-reference/parameter/. 200 - OK

### x-examples
| Field Name | Type | Description |
| :------------- | :------: | :---------- |
| x-examples | [Example Object](https://redocly.com/docs/openapi-visual-reference/example/) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` |

Check warning on line 266 in docs/redoc-vendor-extensions.md

View workflow job for this annotation

GitHub Actions / linkcheck

link checker warning

https://redocly.com/docs/openapi-visual-reference/example/. 200 - OK

#### How to use with Redoc
`x-examples` are rendered in the JSON tab on the right panel in Redoc.
Expand Down
25 changes: 25 additions & 0 deletions e2e/integration/menu.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,31 @@ describe('Menu', () => {
cy.location('hash').should('equal', '#schema/Cat');
});

it('should contains badge schema from x-badges', () => {
cy.contains('h2', 'Add a new pet to the store').scrollIntoView();

cy.contains('h2 > span', 'Beta')
.scrollIntoView()
.wait(100)
.get('[role=menuitem] > label.active')
.children('span[type="badge"]')
.should('have.text', 'Beta');

cy.contains('h2 > span', 'Alpha')
.scrollIntoView()
.wait(100)
.get('[role=menuitem] > label.active')
.children('span[type="badge"]')
.should('have.text', 'Alpha');

cy.contains('h2 > span', 'Gamma')
.scrollIntoView()
.wait(100)
.get('[role=menuitem] > label.active')
.children('span[type="badge"]')
.should('have.text', 'Gamma');
});

it('should contains Cat schema in Pet using x-tags', () => {
cy.contains('[role=menuitem] > label.-depth1', 'pet').click({ force: true });
cy.location('hash').should('equal', '#tag/pet');
Expand Down
4 changes: 2 additions & 2 deletions src/common-elements/shelfs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ export const ShelfIcon = styled(IntShelfIcon)`
}
`;

export const Badge = styled.span<{ type: string }>`
export const Badge = styled.span<{ type: string; color?: string }>`
display: inline-block;
padding: 2px 8px;
margin: 0;
background-color: ${props => props.theme.colors[props.type].main};
background-color: ${props => props.color || props.theme.colors[props.type].main};
color: ${props => props.theme.colors[props.type].contrastText};
font-size: ${props => props.theme.typography.code.fontSize};
vertical-align: middle;
Expand Down
23 changes: 22 additions & 1 deletion src/components/Operation/Operation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,44 @@ 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,
externalDocs,
isWebhook,
httpVerb,
badges,
} = operation;
const hasDescription = !!(description || externalDocs);
const { showWebhookVerb } = React.useContext(OptionsContext);
const badgesBefore = badges.filter(({ position }) => position === 'before');
const badgesAfter = badges.filter(({ position }) => position === 'after');

return (
<OptionsContext.Consumer>
{options => (
<Row {...{ [SECTION_ATTR]: operation.operationHash }} id={operation.operationHash}>
<MiddlePanel>
<H2>
<ShareLink to={operation.id} />
{badgesBefore.map(({ name, color }) => (
<Badge type="primary" key={name} color={color}>
{name}
</Badge>
))}
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
{isWebhook && (
<Badge type="primary">
{' '}
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
</Badge>
)}
{badgesAfter.map(({ name, color }) => (
<Badge type="primary" key={name} color={color}>
{name}
</Badge>
))}
</H2>
{options.pathInMiddlePanel && !isWebhook && (
<Endpoint operation={operation} inverted={true} />
Expand Down
6 changes: 6 additions & 0 deletions src/components/SideMenu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ export const OperationMenuItemContent = observer((props: OperationMenuItemConten
$deprecated={item.deprecated}
ref={ref}
>
{item.badges &&
item.badges?.map(({ name, color }) => (
<OperationBadge type="badge" color={color} key={name}>
{name}
</OperationBadge>
))}
{item.isWebhook ? (
<OperationBadge type="hook">
{showWebhookVerb ? item.httpVerb : l('webhook')}
Expand Down
6 changes: 3 additions & 3 deletions src/components/SideMenu/styled.elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { darken } from 'polished';
import { deprecatedCss, ShelfIcon } from '../../common-elements';
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';

export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
export const OperationBadge = styled.span.attrs((props: { type: string; color?: string }) => ({
className: `operation-type ${props.type}`,
}))<{ type: string }>`
}))<{ type: string; color?: string }>`
width: 9ex;
display: inline-block;
height: ${props => props.theme.typography.code.fontSize};
line-height: ${props => props.theme.typography.code.fontSize};
background-color: #333;
background-color: ${props => props.color || '#333'};
border-radius: 3px;
background-repeat: no-repeat;
background-position: 6px 4px;
Expand Down
14 changes: 13 additions & 1 deletion src/services/models/Operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
OpenAPIXBadges,
OpenAPIXCodeSample,
} from '../../types';
import type { OpenAPIParser } from '../OpenAPIParser';
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import type { MediaContentModel } from './MediaContent';
Expand Down Expand Up @@ -71,6 +76,7 @@ export class OperationModel implements IMenuItem {
operationId?: string;
operationHash?: string;
httpVerb: string;
badges: OpenAPIXBadges[];
deprecated: boolean;
path: string;
servers: OpenAPIServer[];
Expand Down Expand Up @@ -112,6 +118,12 @@ export class OperationModel implements IMenuItem {
: options.sideNavStyle === SideNavStyleEnum.PathOnly
? this.path
: this.name;
this.badges =
operationSpec['x-badges']?.map(({ name, color, position }) => ({
name,
color: color,
position: position || 'after',
})) || [];

if (this.isCallback) {
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.
Expand Down
7 changes: 7 additions & 0 deletions src/types/open-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ export interface OpenAPIXCodeSample {
source: string;
}

export interface OpenAPIXBadges {
name: string;
color?: string;
position?: 'before' | 'after';
}

export interface OpenAPIOperation {
tags?: string[];
summary?: string;
Expand All @@ -85,6 +91,7 @@ export interface OpenAPIOperation {
servers?: OpenAPIServer[];
'x-codeSamples'?: OpenAPIXCodeSample[];
'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated
'x-badges'?: OpenAPIXBadges[];
}

export interface OpenAPIParameter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ and standard method from web, mobile and desktop applications.
"tags": [
"pet",
],
"x-badges": [
{
"color": "purple",
"name": "Beta",
"position": "before",
},
],
"x-codeSamples": [
{
"lang": "C#",
Expand Down Expand Up @@ -645,6 +652,12 @@ try {
"tags": [
"pet",
],
"x-badges": [
{
"color": "purple",
"name": "Alpha",
},
],
"x-codeSamples": [
{
"lang": "PHP",
Expand Down Expand Up @@ -883,6 +896,11 @@ try {
"tags": [
"pet",
],
"x-badges": [
{
"name": "Gamma",
},
],
},
"post": {
"description": "",
Expand Down
1 change: 1 addition & 0 deletions src/utils/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ export function isRedocExtension(key: string): boolean {
'x-servers': true,
'x-tagGroups': true,
'x-traitTag': true,
'x-badges': true,
'x-additionalPropertiesName': true,
'x-explicitMappingOnly': true,
};
Expand Down
Loading