Skip to content

Commit 64f1877

Browse files
AlexVarchukmm-gmbdJLekawa
authored
feat: add support x-badges (#2605)
* feat: add support x-badges Co-authored-by: Max Mueller <[email protected]> * chore: try to fix e2e tests * chore: try to fix e2e tests part 2 * Update docs/redoc-vendor-extensions.md --------- Co-authored-by: Max Mueller <[email protected]> Co-authored-by: Jacek Łękawa <[email protected]>
1 parent 1cceed4 commit 64f1877

File tree

13 files changed

+122
-8
lines changed

13 files changed

+122
-8
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Redoc is provided as a CLI tool (also distributed as a Docker image), HTML tag,
4949
If you have Node installed, quickly generate documentation using `npx`:
5050

5151
```bash
52-
npx @redocly/cli build-docs openapi.yaml
52+
npx @redocly/cli build-docs openapi.yaml
5353
```
5454

5555
The tool outputs by default to a file named `redoc-static.html` that you can open in your browser.
@@ -116,6 +116,7 @@ Redoc uses the following [specification extensions](https://redocly.com/docs/api
116116
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
117117
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for tags that refer to non-navigation properties like Pagination, Rate-Limits, etc
118118
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
119+
* [`x-badges`](docs/redoc-vendor-extensions.md#x-badges) - specify operation badges
119120
* [`x-examples`](docs/redoc-vendor-extensions.md#x-examples) - specify JSON example for requests
120121
* [`x-nullable`](docs/redoc-vendor-extensions.md#x-nullable) - mark schema param as a nullable
121122
* [`x-displayName`](docs/redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories

demo/museum.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ paths:
2222
operationId: getMuseumHours
2323
tags:
2424
- Operations
25+
x-badges:
26+
- name: 'Beta'
27+
position: before
28+
color: purple
2529
parameters:
2630
- $ref: '#/components/parameters/StartDate'
2731
- $ref: '#/components/parameters/PaginationPage'
@@ -64,6 +68,9 @@ paths:
6468
summary: Create special event
6569
tags:
6670
- Events
71+
x-badges:
72+
- name: 'Alpha'
73+
color: purple
6774
requestBody:
6875
required: true
6976
content:
@@ -92,6 +99,8 @@ paths:
9299
description: Return a list of upcoming special events at the museum.
93100
security: []
94101
operationId: listSpecialEvents
102+
x-badges:
103+
- name: 'Gamma'
95104
tags:
96105
- Events
97106
parameters:

demo/openapi.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ paths:
106106
post:
107107
tags:
108108
- pet
109+
x-badges:
110+
- name: 'Beta'
111+
position: before
112+
color: purple
109113
summary: Add a new pet to the store
110114
description: Add new pet to the store inventory.
111115
operationId: addPet
@@ -150,6 +154,9 @@ paths:
150154
put:
151155
tags:
152156
- pet
157+
x-badges:
158+
- name: 'Alpha'
159+
color: purple
153160
summary: Update an existing pet
154161
description: ''
155162
operationId: updatePet
@@ -183,6 +190,8 @@ paths:
183190
get:
184191
tags:
185192
- pet
193+
x-badges:
194+
- name: 'Gamma'
186195
summary: Find pet by ID
187196
description: Returns a single pet
188197
operationId: getPetById

docs/redoc-vendor-extensions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ lang: JavaScript
252252
source: console.log('Hello World');
253253
```
254254

255+
### x-badges
256+
| Field Name | Type | Description |
257+
| :------------- | :------: | :---------- |
258+
| 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 |
259+
255260
## Parameter Object
256261
Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/)
257262

e2e/integration/menu.e2e.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,31 @@ describe('Menu', () => {
5252
cy.location('hash').should('equal', '#schema/Cat');
5353
});
5454

55+
it('should contains badge schema from x-badges', () => {
56+
cy.contains('h2', 'Add a new pet to the store').scrollIntoView();
57+
58+
cy.contains('h2 > span', 'Beta')
59+
.scrollIntoView()
60+
.wait(100)
61+
.get('[role=menuitem] > label.active')
62+
.children('span[type="badge"]')
63+
.should('have.text', 'Beta');
64+
65+
cy.contains('h2 > span', 'Alpha')
66+
.scrollIntoView()
67+
.wait(100)
68+
.get('[role=menuitem] > label.active')
69+
.children('span[type="badge"]')
70+
.should('have.text', 'Alpha');
71+
72+
cy.contains('h2 > span', 'Gamma')
73+
.scrollIntoView()
74+
.wait(100)
75+
.get('[role=menuitem] > label.active')
76+
.children('span[type="badge"]')
77+
.should('have.text', 'Gamma');
78+
});
79+
5580
it('should contains Cat schema in Pet using x-tags', () => {
5681
cy.contains('[role=menuitem] > label.-depth1', 'pet').click({ force: true });
5782
cy.location('hash').should('equal', '#tag/pet');

src/common-elements/shelfs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ export const ShelfIcon = styled(IntShelfIcon)`
4747
}
4848
`;
4949

50-
export const Badge = styled.span<{ type: string }>`
50+
export const Badge = styled.span<{ type: string; color?: string }>`
5151
display: inline-block;
5252
padding: 2px 8px;
5353
margin: 0;
54-
background-color: ${props => props.theme.colors[props.type].main};
54+
background-color: ${props => props.color || props.theme.colors[props.type].main};
5555
color: ${props => props.theme.colors[props.type].contrastText};
5656
font-size: ${props => props.theme.typography.code.fontSize};
5757
vertical-align: middle;

src/components/Operation/Operation.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,44 @@ export interface OperationProps {
2828
}
2929

3030
export const Operation = observer(({ operation }: OperationProps): JSX.Element => {
31-
const { name: summary, description, deprecated, externalDocs, isWebhook, httpVerb } = operation;
31+
const {
32+
name: summary,
33+
description,
34+
deprecated,
35+
externalDocs,
36+
isWebhook,
37+
httpVerb,
38+
badges,
39+
} = operation;
3240
const hasDescription = !!(description || externalDocs);
3341
const { showWebhookVerb } = React.useContext(OptionsContext);
42+
const badgesBefore = badges.filter(({ position }) => position === 'before');
43+
const badgesAfter = badges.filter(({ position }) => position === 'after');
44+
3445
return (
3546
<OptionsContext.Consumer>
3647
{options => (
3748
<Row {...{ [SECTION_ATTR]: operation.operationHash }} id={operation.operationHash}>
3849
<MiddlePanel>
3950
<H2>
4051
<ShareLink to={operation.id} />
52+
{badgesBefore.map(({ name, color }) => (
53+
<Badge type="primary" key={name} color={color}>
54+
{name}
55+
</Badge>
56+
))}
4157
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
4258
{isWebhook && (
4359
<Badge type="primary">
4460
{' '}
4561
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
4662
</Badge>
4763
)}
64+
{badgesAfter.map(({ name, color }) => (
65+
<Badge type="primary" key={name} color={color}>
66+
{name}
67+
</Badge>
68+
))}
4869
</H2>
4970
{options.pathInMiddlePanel && !isWebhook && (
5071
<Endpoint operation={operation} inverted={true} />

src/components/SideMenu/MenuItem.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ export const OperationMenuItemContent = observer((props: OperationMenuItemConten
101101
$deprecated={item.deprecated}
102102
ref={ref}
103103
>
104+
{item.badges &&
105+
item.badges?.map(({ name, color }) => (
106+
<OperationBadge type="badge" color={color} key={name}>
107+
{name}
108+
</OperationBadge>
109+
))}
104110
{item.isWebhook ? (
105111
<OperationBadge type="hook">
106112
{showWebhookVerb ? item.httpVerb : l('webhook')}

src/components/SideMenu/styled.elements.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { darken } from 'polished';
44
import { deprecatedCss, ShelfIcon } from '../../common-elements';
55
import styled, { css, media, ResolvedThemeInterface } from '../../styled-components';
66

7-
export const OperationBadge = styled.span.attrs((props: { type: string }) => ({
7+
export const OperationBadge = styled.span.attrs((props: { type: string; color?: string }) => ({
88
className: `operation-type ${props.type}`,
9-
}))<{ type: string }>`
9+
}))<{ type: string; color?: string }>`
1010
width: 9ex;
1111
display: inline-block;
1212
height: ${props => props.theme.typography.code.fontSize};
1313
line-height: ${props => props.theme.typography.code.fontSize};
14-
background-color: #333;
14+
background-color: ${props => props.color || '#333'};
1515
border-radius: 3px;
1616
background-repeat: no-repeat;
1717
background-position: 6px 4px;

src/services/models/Operation.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import { RequestBodyModel } from './RequestBody';
2020
import { ResponseModel } from './Response';
2121
import { SideNavStyleEnum } from '../types';
2222

23-
import type { OpenAPIExternalDocumentation, OpenAPIServer, OpenAPIXCodeSample } from '../../types';
23+
import type {
24+
OpenAPIExternalDocumentation,
25+
OpenAPIServer,
26+
OpenAPIXBadges,
27+
OpenAPIXCodeSample,
28+
} from '../../types';
2429
import type { OpenAPIParser } from '../OpenAPIParser';
2530
import type { RedocNormalizedOptions } from '../RedocNormalizedOptions';
2631
import type { MediaContentModel } from './MediaContent';
@@ -71,6 +76,7 @@ export class OperationModel implements IMenuItem {
7176
operationId?: string;
7277
operationHash?: string;
7378
httpVerb: string;
79+
badges: OpenAPIXBadges[];
7480
deprecated: boolean;
7581
path: string;
7682
servers: OpenAPIServer[];
@@ -112,6 +118,12 @@ export class OperationModel implements IMenuItem {
112118
: options.sideNavStyle === SideNavStyleEnum.PathOnly
113119
? this.path
114120
: this.name;
121+
this.badges =
122+
operationSpec['x-badges']?.map(({ name, color, position }) => ({
123+
name,
124+
color: color,
125+
position: position || 'after',
126+
})) || [];
115127

116128
if (this.isCallback) {
117129
// NOTE: Callbacks by default should not inherit the specification's global `security` definition.

src/types/open-api.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ export interface OpenAPIXCodeSample {
7070
source: string;
7171
}
7272

73+
export interface OpenAPIXBadges {
74+
name: string;
75+
color?: string;
76+
position?: 'before' | 'after';
77+
}
78+
7379
export interface OpenAPIOperation {
7480
tags?: string[];
7581
summary?: string;
@@ -85,6 +91,7 @@ export interface OpenAPIOperation {
8591
servers?: OpenAPIServer[];
8692
'x-codeSamples'?: OpenAPIXCodeSample[];
8793
'x-code-samples'?: OpenAPIXCodeSample[]; // deprecated
94+
'x-badges'?: OpenAPIXBadges[];
8895
}
8996

9097
export interface OpenAPIParameter {

src/utils/__tests__/__snapshots__/loadAndBundleSpec.test.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,13 @@ and standard method from web, mobile and desktop applications.
581581
"tags": [
582582
"pet",
583583
],
584+
"x-badges": [
585+
{
586+
"color": "purple",
587+
"name": "Beta",
588+
"position": "before",
589+
},
590+
],
584591
"x-codeSamples": [
585592
{
586593
"lang": "C#",
@@ -645,6 +652,12 @@ try {
645652
"tags": [
646653
"pet",
647654
],
655+
"x-badges": [
656+
{
657+
"color": "purple",
658+
"name": "Alpha",
659+
},
660+
],
648661
"x-codeSamples": [
649662
{
650663
"lang": "PHP",
@@ -883,6 +896,11 @@ try {
883896
"tags": [
884897
"pet",
885898
],
899+
"x-badges": [
900+
{
901+
"name": "Gamma",
902+
},
903+
],
886904
},
887905
"post": {
888906
"description": "",

src/utils/openapi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ export function isRedocExtension(key: string): boolean {
660660
'x-servers': true,
661661
'x-tagGroups': true,
662662
'x-traitTag': true,
663+
'x-badges': true,
663664
'x-additionalPropertiesName': true,
664665
'x-explicitMappingOnly': true,
665666
};

0 commit comments

Comments
 (0)