Skip to content

Commit 30db4df

Browse files
committed
fix: unify redoc config
1 parent 59ee73f commit 30db4df

File tree

10 files changed

+83
-63
lines changed

10 files changed

+83
-63
lines changed

demo/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class DemoApp extends React.Component<
122122
<RedocStandalone
123123
spec={this.state.spec}
124124
specUrl={proxiedUrl}
125-
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
125+
options={{ scrollYOffset: 'nav', sanitize: true }}
126126
/>
127127
</>
128128
);

src/components/ApiInfo/ApiInfo.tsx

+18-19
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,14 @@ export interface ApiInfoProps {
2222

2323
@observer
2424
export class ApiInfo extends React.Component<ApiInfoProps> {
25-
handleDownloadClick = e => {
26-
if (!e.target.href) {
27-
e.target.href = this.props.store.spec.info.downloadLink;
28-
}
29-
};
30-
3125
render() {
3226
const { store } = this.props;
3327
const { info, externalDocs } = store.spec;
34-
const hideDownloadButton = store.options.hideDownloadButton;
35-
36-
const downloadFilename = info.downloadFileName;
37-
const downloadLink = info.downloadLink;
28+
const hideDownloadButtons = store.options.hideDownloadButtons;
3829

30+
// FIXME: use downloadUrls
31+
const downloadUrls = info.downloadUrls;
32+
console.log(downloadUrls);
3933
const license =
4034
(info.license && (
4135
<InfoSpan>
@@ -83,17 +77,22 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
8377
<ApiHeader>
8478
{info.title} {version}
8579
</ApiHeader>
86-
{!hideDownloadButton && (
80+
{!hideDownloadButtons && (
8781
<p>
8882
{l('downloadSpecification')}:
89-
<DownloadButton
90-
download={downloadFilename || true}
91-
target="_blank"
92-
href={downloadLink}
93-
onClick={this.handleDownloadClick}
94-
>
95-
{l('download')}
96-
</DownloadButton>
83+
{downloadUrls?.map(({ title, url }) => {
84+
return (
85+
<DownloadButton
86+
download={title}
87+
target="_blank"
88+
href={url}
89+
rel="noreferrer"
90+
key={title}
91+
>
92+
{downloadUrls.length > 1 ? title : l('download')}
93+
</DownloadButton>
94+
);
95+
})}
9796
</p>
9897
)}
9998
<StyledMarkdownBlock>

src/components/JsonViewer/JsonViewer.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const Json = (props: JsonProps) => {
4545
// tslint:disable-next-line
4646
ref={node => setNode(node!)}
4747
dangerouslySetInnerHTML={{
48-
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
48+
__html: jsonToHTML(props.data, options.jsonSamplesExpandLevel),
4949
}}
5050
/>
5151
)}

src/components/Markdown/SanitizedMdBlock.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
1010
display: inline;
1111
`;
1212

13-
const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
13+
const sanitize = (sanitize, html) => (sanitize ? DOMPurify.sanitize(html) : html);
1414

1515
export function SanitizedMarkdownHTML({
1616
inline,
@@ -25,7 +25,7 @@ export function SanitizedMarkdownHTML({
2525
<Wrap
2626
className={'redoc-markdown ' + (rest.className || '')}
2727
dangerouslySetInnerHTML={{
28-
__html: sanitize(options.untrustedSpec, rest.html),
28+
__html: sanitize(options.sanitize, rest.html),
2929
}}
3030
data-role={rest['data-role']}
3131
{...rest}

src/components/Schema/ObjectSchema.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const ObjectSchema = observer(
2727
skipWriteOnly,
2828
level,
2929
}: ObjectSchemaProps) => {
30-
const { expandSingleSchemaField, showObjectSchemaExamples, schemaExpansionLevel } =
30+
const { expandSingleSchemaField, showObjectSchemaExamples, schemasExpansionLevel } =
3131
React.useContext(OptionsContext);
3232

3333
const filteredFields = React.useMemo(
@@ -45,7 +45,7 @@ export const ObjectSchema = observer(
4545
);
4646

4747
const expandByDefault =
48-
(expandSingleSchemaField && filteredFields.length === 1) || schemaExpansionLevel >= level!;
48+
(expandSingleSchemaField && filteredFields.length === 1) || schemasExpansionLevel >= level!;
4949

5050
return (
5151
<PropertiesTable>

src/services/RedocNormalizedOptions.ts

+35-20
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,47 @@ import { setRedocLabels } from './Labels';
66
import { SideNavStyleEnum } from './types';
77
import type { LabelsConfigRaw, MDXComponentMeta } from './types';
88

9+
export type DownloadUrlsConfig = {
10+
title?: string;
11+
url: string;
12+
}[];
13+
914
export interface RedocRawOptions {
1015
theme?: ThemeInterface;
1116
scrollYOffset?: number | string | (() => number);
1217
hideHostname?: boolean | string;
1318
expandResponses?: string | 'all';
14-
requiredPropsFirst?: boolean | string;
19+
requiredPropsFirst?: boolean | string; // remove in next major release
20+
sortRequiredPropsFirst?: boolean | string;
1521
sortPropsAlphabetically?: boolean | string;
1622
sortEnumValuesAlphabetically?: boolean | string;
1723
sortOperationsAlphabetically?: boolean | string;
1824
sortTagsAlphabetically?: boolean | string;
1925
nativeScrollbars?: boolean | string;
2026
pathInMiddlePanel?: boolean | string;
21-
untrustedSpec?: boolean | string;
27+
untrustedSpec?: boolean | string; // remove in next major release
28+
sanitize?: boolean | string;
2229
hideLoading?: boolean | string;
23-
hideDownloadButton?: boolean | string;
30+
hideDownloadButton?: boolean | string; // remove in next major release
31+
hideDownloadButtons?: boolean | string;
2432
downloadFileName?: string;
2533
downloadDefinitionUrl?: string;
34+
downloadUrls?: DownloadUrlsConfig;
2635
disableSearch?: boolean | string;
2736
onlyRequiredInSamples?: boolean | string;
2837
showExtensions?: boolean | string | string[];
2938
sideNavStyle?: SideNavStyleEnum;
3039
hideSingleRequestSampleTab?: boolean | string;
3140
hideRequestPayloadSample?: boolean;
3241
menuToggle?: boolean | string;
33-
jsonSampleExpandLevel?: number | string | 'all';
42+
jsonSampleExpandLevel?: number | string | 'all'; // remove in next major release
43+
jsonSamplesExpandLevel?: number | string | 'all';
3444
hideSchemaTitles?: boolean | string;
3545
simpleOneOfTypeLabel?: boolean | string;
3646
payloadSampleIdx?: number;
3747
expandSingleSchemaField?: boolean | string;
38-
schemaExpansionLevel?: number | string | 'all';
48+
schemaExpansionLevel?: number | string | 'all'; // remove in next major release
49+
schemasExpansionLevel?: number | string | 'all';
3950
showObjectSchemaExamples?: boolean | string;
4051
showSecuritySchemeType?: boolean;
4152
hideSecuritySection?: boolean;
@@ -216,31 +227,30 @@ export class RedocNormalizedOptions {
216227
scrollYOffset: () => number;
217228
hideHostname: boolean;
218229
expandResponses: { [code: string]: boolean } | 'all';
219-
requiredPropsFirst: boolean;
230+
sortRequiredPropsFirst: boolean;
220231
sortPropsAlphabetically: boolean;
221232
sortEnumValuesAlphabetically: boolean;
222233
sortOperationsAlphabetically: boolean;
223234
sortTagsAlphabetically: boolean;
224235
nativeScrollbars: boolean;
225236
pathInMiddlePanel: boolean;
226-
untrustedSpec: boolean;
227-
hideDownloadButton: boolean;
228-
downloadFileName?: string;
229-
downloadDefinitionUrl?: string;
237+
sanitize: boolean;
238+
hideDownloadButtons: boolean;
239+
downloadUrls?: DownloadUrlsConfig;
230240
disableSearch: boolean;
231241
onlyRequiredInSamples: boolean;
232242
showExtensions: boolean | string[];
233243
sideNavStyle: SideNavStyleEnum;
234244
hideSingleRequestSampleTab: boolean;
235245
hideRequestPayloadSample: boolean;
236246
menuToggle: boolean;
237-
jsonSampleExpandLevel: number;
247+
jsonSamplesExpandLevel: number;
238248
enumSkipQuotes: boolean;
239249
hideSchemaTitles: boolean;
240250
simpleOneOfTypeLabel: boolean;
241251
payloadSampleIdx: number;
242252
expandSingleSchemaField: boolean;
243-
schemaExpansionLevel: number;
253+
schemasExpansionLevel: number;
244254
showObjectSchemaExamples: boolean;
245255
showSecuritySchemeType?: boolean;
246256
hideSecuritySection?: boolean;
@@ -288,33 +298,38 @@ export class RedocNormalizedOptions {
288298
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
289299
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
290300
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
291-
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
301+
this.sortRequiredPropsFirst = argValueToBoolean(
302+
raw.sortRequiredPropsFirst || raw.requiredPropsFirst,
303+
);
292304
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
293305
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
294306
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
295307
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
296308
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
297309
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
298-
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
299-
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
300-
this.downloadFileName = raw.downloadFileName;
301-
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
310+
this.sanitize = argValueToBoolean(raw.sanitize || raw.untrustedSpec);
311+
this.hideDownloadButtons = argValueToBoolean(raw.hideDownloadButtons || raw.hideDownloadButton);
312+
this.downloadUrls =
313+
raw.downloadUrls ||
314+
([{ title: raw.downloadFileName, url: raw.downloadDefinitionUrl }] as DownloadUrlsConfig);
302315
this.disableSearch = argValueToBoolean(raw.disableSearch);
303316
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
304317
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
305318
this.sideNavStyle = RedocNormalizedOptions.normalizeSideNavStyle(raw.sideNavStyle);
306319
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
307320
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
308321
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
309-
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
310-
raw.jsonSampleExpandLevel,
322+
this.jsonSamplesExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
323+
raw.jsonSamplesExpandLevel || raw.jsonSampleExpandLevel,
311324
);
312325
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
313326
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
314327
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
315328
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
316329
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
317-
this.schemaExpansionLevel = argValueToExpandLevel(raw.schemaExpansionLevel);
330+
this.schemasExpansionLevel = argValueToExpandLevel(
331+
raw.schemasExpansionLevel || raw.schemaExpansionLevel,
332+
);
318333
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
319334
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
320335
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);

src/services/__tests__/models/ApiInfo.test.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ describe('Models', () => {
139139
} as any;
140140

141141
const opts = new RedocNormalizedOptions({
142-
downloadDefinitionUrl: 'https:test.com/filename.yaml',
142+
downloadUrls: [{ title: 'Openapi description', url: 'https:test.com/filename.yaml' }],
143143
});
144144
const info = new ApiInfoModel(parser, opts);
145145
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
@@ -160,6 +160,13 @@ describe('Models', () => {
160160
const info = new ApiInfoModel(parser, opts);
161161
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
162162
expect(info.downloadFileName).toEqual('test.yaml');
163+
164+
const opts2 = new RedocNormalizedOptions({
165+
downloadUrls: [{ title: 'test.yaml', url: 'https:test.com/filename.yaml' }],
166+
});
167+
const info2 = new ApiInfoModel(parser, opts2);
168+
expect(info2.downloadLink).toEqual('https:test.com/filename.yaml');
169+
expect(info2.downloadFileName).toEqual('test.yaml');
163170
});
164171
});
165172
});

src/services/models/ApiInfo.ts

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
22
import { IS_BROWSER } from '../../utils/';
33
import type { OpenAPIParser } from '../OpenAPIParser';
4-
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
4+
import { DownloadUrlsConfig, RedocNormalizedOptions } from '../RedocNormalizedOptions';
55

66
export class ApiInfoModel implements OpenAPIInfo {
77
title: string;
@@ -13,8 +13,7 @@ export class ApiInfoModel implements OpenAPIInfo {
1313
contact?: OpenAPIContact;
1414
license?: OpenAPILicense;
1515

16-
downloadLink?: string;
17-
downloadFileName?: string;
16+
downloadUrls?: DownloadUrlsConfig;
1817

1918
constructor(
2019
private parser: OpenAPIParser,
@@ -29,13 +28,20 @@ export class ApiInfoModel implements OpenAPIInfo {
2928
this.description = this.description.substring(0, firstHeadingLinePos);
3029
}
3130

32-
this.downloadLink = this.getDownloadLink();
33-
this.downloadFileName = this.getDownloadFileName();
31+
this.downloadUrls = this.getDownloadUrls();
32+
}
33+
private getDownloadUrls(): DownloadUrlsConfig | undefined {
34+
return this.options.downloadUrls
35+
?.map(({ title, url }) => ({
36+
title: title || 'openapi.json',
37+
url: this.getDownloadLink(url) || '',
38+
}))
39+
.filter(({ title, url }) => title && url);
3440
}
3541

36-
private getDownloadLink(): string | undefined {
37-
if (this.options.downloadDefinitionUrl) {
38-
return this.options.downloadDefinitionUrl;
42+
private getDownloadLink(url?: string): string | undefined {
43+
if (url) {
44+
return url;
3945
}
4046

4147
if (this.parser.specUrl) {
@@ -49,11 +55,4 @@ export class ApiInfoModel implements OpenAPIInfo {
4955
return window.URL.createObjectURL(blob);
5056
}
5157
}
52-
53-
private getDownloadFileName(): string | undefined {
54-
if (!this.parser.specUrl && !this.options.downloadDefinitionUrl) {
55-
return this.options.downloadFileName || 'openapi.json';
56-
}
57-
return this.options.downloadFileName;
58-
}
5958
}

src/services/models/Operation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ export class OperationModel implements IMenuItem {
247247
if (this.options.sortPropsAlphabetically) {
248248
return sortByField(_parameters, 'name');
249249
}
250-
if (this.options.requiredPropsFirst) {
250+
if (this.options.sortRequiredPropsFirst) {
251251
return sortByRequired(_parameters);
252252
}
253253

src/services/models/Schema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ function buildFields(
463463
if (options.sortPropsAlphabetically) {
464464
fields = sortByField(fields, 'name');
465465
}
466-
if (options.requiredPropsFirst) {
466+
if (options.sortRequiredPropsFirst) {
467467
// if not sort alphabetically sort in the order from required keyword
468468
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
469469
}

0 commit comments

Comments
 (0)