Skip to content

Commit

Permalink
fix: unify redoc config
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexVarchuk committed Jan 15, 2025
1 parent 59ee73f commit 30db4df
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 63 deletions.
2 changes: 1 addition & 1 deletion demo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class DemoApp extends React.Component<
<RedocStandalone
spec={this.state.spec}
specUrl={proxiedUrl}
options={{ scrollYOffset: 'nav', untrustedSpec: true }}
options={{ scrollYOffset: 'nav', sanitize: true }}
/>
</>
);
Expand Down
37 changes: 18 additions & 19 deletions src/components/ApiInfo/ApiInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,14 @@ export interface ApiInfoProps {

@observer
export class ApiInfo extends React.Component<ApiInfoProps> {
handleDownloadClick = e => {
if (!e.target.href) {
e.target.href = this.props.store.spec.info.downloadLink;
}
};

render() {
const { store } = this.props;
const { info, externalDocs } = store.spec;
const hideDownloadButton = store.options.hideDownloadButton;

const downloadFilename = info.downloadFileName;
const downloadLink = info.downloadLink;
const hideDownloadButtons = store.options.hideDownloadButtons;

// FIXME: use downloadUrls
const downloadUrls = info.downloadUrls;
console.log(downloadUrls);
const license =
(info.license && (
<InfoSpan>
Expand Down Expand Up @@ -83,17 +77,22 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
<ApiHeader>
{info.title} {version}
</ApiHeader>
{!hideDownloadButton && (
{!hideDownloadButtons && (
<p>
{l('downloadSpecification')}:
<DownloadButton
download={downloadFilename || true}
target="_blank"
href={downloadLink}
onClick={this.handleDownloadClick}
>
{l('download')}
</DownloadButton>
{downloadUrls?.map(({ title, url }) => {
return (
<DownloadButton
download={title}
target="_blank"
href={url}
rel="noreferrer"
key={title}
>
{downloadUrls.length > 1 ? title : l('download')}
</DownloadButton>
);
})}
</p>
)}
<StyledMarkdownBlock>
Expand Down
2 changes: 1 addition & 1 deletion src/components/JsonViewer/JsonViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Json = (props: JsonProps) => {
// tslint:disable-next-line
ref={node => setNode(node!)}
dangerouslySetInnerHTML={{
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
__html: jsonToHTML(props.data, options.jsonSamplesExpandLevel),
}}
/>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Markdown/SanitizedMdBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
display: inline;
`;

const sanitize = (untrustedSpec, html) => (untrustedSpec ? DOMPurify.sanitize(html) : html);
const sanitize = (sanitize, html) => (sanitize ? DOMPurify.sanitize(html) : html);

export function SanitizedMarkdownHTML({
inline,
Expand All @@ -25,7 +25,7 @@ export function SanitizedMarkdownHTML({
<Wrap
className={'redoc-markdown ' + (rest.className || '')}
dangerouslySetInnerHTML={{
__html: sanitize(options.untrustedSpec, rest.html),
__html: sanitize(options.sanitize, rest.html),
}}
data-role={rest['data-role']}
{...rest}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Schema/ObjectSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const ObjectSchema = observer(
skipWriteOnly,
level,
}: ObjectSchemaProps) => {
const { expandSingleSchemaField, showObjectSchemaExamples, schemaExpansionLevel } =
const { expandSingleSchemaField, showObjectSchemaExamples, schemasExpansionLevel } =
React.useContext(OptionsContext);

const filteredFields = React.useMemo(
Expand All @@ -45,7 +45,7 @@ export const ObjectSchema = observer(
);

const expandByDefault =
(expandSingleSchemaField && filteredFields.length === 1) || schemaExpansionLevel >= level!;
(expandSingleSchemaField && filteredFields.length === 1) || schemasExpansionLevel >= level!;

return (
<PropertiesTable>
Expand Down
55 changes: 35 additions & 20 deletions src/services/RedocNormalizedOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,47 @@ import { setRedocLabels } from './Labels';
import { SideNavStyleEnum } from './types';
import type { LabelsConfigRaw, MDXComponentMeta } from './types';

export type DownloadUrlsConfig = {
title?: string;
url: string;
}[];

export interface RedocRawOptions {
theme?: ThemeInterface;
scrollYOffset?: number | string | (() => number);
hideHostname?: boolean | string;
expandResponses?: string | 'all';
requiredPropsFirst?: boolean | string;
requiredPropsFirst?: boolean | string; // remove in next major release
sortRequiredPropsFirst?: boolean | string;
sortPropsAlphabetically?: boolean | string;
sortEnumValuesAlphabetically?: boolean | string;
sortOperationsAlphabetically?: boolean | string;
sortTagsAlphabetically?: boolean | string;
nativeScrollbars?: boolean | string;
pathInMiddlePanel?: boolean | string;
untrustedSpec?: boolean | string;
untrustedSpec?: boolean | string; // remove in next major release
sanitize?: boolean | string;
hideLoading?: boolean | string;
hideDownloadButton?: boolean | string;
hideDownloadButton?: boolean | string; // remove in next major release
hideDownloadButtons?: boolean | string;
downloadFileName?: string;
downloadDefinitionUrl?: string;
downloadUrls?: DownloadUrlsConfig;
disableSearch?: boolean | string;
onlyRequiredInSamples?: boolean | string;
showExtensions?: boolean | string | string[];
sideNavStyle?: SideNavStyleEnum;
hideSingleRequestSampleTab?: boolean | string;
hideRequestPayloadSample?: boolean;
menuToggle?: boolean | string;
jsonSampleExpandLevel?: number | string | 'all';
jsonSampleExpandLevel?: number | string | 'all'; // remove in next major release
jsonSamplesExpandLevel?: number | string | 'all';
hideSchemaTitles?: boolean | string;
simpleOneOfTypeLabel?: boolean | string;
payloadSampleIdx?: number;
expandSingleSchemaField?: boolean | string;
schemaExpansionLevel?: number | string | 'all';
schemaExpansionLevel?: number | string | 'all'; // remove in next major release
schemasExpansionLevel?: number | string | 'all';
showObjectSchemaExamples?: boolean | string;
showSecuritySchemeType?: boolean;
hideSecuritySection?: boolean;
Expand Down Expand Up @@ -216,31 +227,30 @@ export class RedocNormalizedOptions {
scrollYOffset: () => number;
hideHostname: boolean;
expandResponses: { [code: string]: boolean } | 'all';
requiredPropsFirst: boolean;
sortRequiredPropsFirst: boolean;
sortPropsAlphabetically: boolean;
sortEnumValuesAlphabetically: boolean;
sortOperationsAlphabetically: boolean;
sortTagsAlphabetically: boolean;
nativeScrollbars: boolean;
pathInMiddlePanel: boolean;
untrustedSpec: boolean;
hideDownloadButton: boolean;
downloadFileName?: string;
downloadDefinitionUrl?: string;
sanitize: boolean;
hideDownloadButtons: boolean;
downloadUrls?: DownloadUrlsConfig;
disableSearch: boolean;
onlyRequiredInSamples: boolean;
showExtensions: boolean | string[];
sideNavStyle: SideNavStyleEnum;
hideSingleRequestSampleTab: boolean;
hideRequestPayloadSample: boolean;
menuToggle: boolean;
jsonSampleExpandLevel: number;
jsonSamplesExpandLevel: number;
enumSkipQuotes: boolean;
hideSchemaTitles: boolean;
simpleOneOfTypeLabel: boolean;
payloadSampleIdx: number;
expandSingleSchemaField: boolean;
schemaExpansionLevel: number;
schemasExpansionLevel: number;
showObjectSchemaExamples: boolean;
showSecuritySchemeType?: boolean;
hideSecuritySection?: boolean;
Expand Down Expand Up @@ -288,33 +298,38 @@ export class RedocNormalizedOptions {
this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
this.sortRequiredPropsFirst = argValueToBoolean(
raw.sortRequiredPropsFirst || raw.requiredPropsFirst,
);
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
this.sortEnumValuesAlphabetically = argValueToBoolean(raw.sortEnumValuesAlphabetically);
this.sortOperationsAlphabetically = argValueToBoolean(raw.sortOperationsAlphabetically);
this.sortTagsAlphabetically = argValueToBoolean(raw.sortTagsAlphabetically);
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);
this.untrustedSpec = argValueToBoolean(raw.untrustedSpec);
this.hideDownloadButton = argValueToBoolean(raw.hideDownloadButton);
this.downloadFileName = raw.downloadFileName;
this.downloadDefinitionUrl = raw.downloadDefinitionUrl;
this.sanitize = argValueToBoolean(raw.sanitize || raw.untrustedSpec);
this.hideDownloadButtons = argValueToBoolean(raw.hideDownloadButtons || raw.hideDownloadButton);
this.downloadUrls =
raw.downloadUrls ||
([{ title: raw.downloadFileName, url: raw.downloadDefinitionUrl }] as DownloadUrlsConfig);
this.disableSearch = argValueToBoolean(raw.disableSearch);
this.onlyRequiredInSamples = argValueToBoolean(raw.onlyRequiredInSamples);
this.showExtensions = RedocNormalizedOptions.normalizeShowExtensions(raw.showExtensions);
this.sideNavStyle = RedocNormalizedOptions.normalizeSideNavStyle(raw.sideNavStyle);
this.hideSingleRequestSampleTab = argValueToBoolean(raw.hideSingleRequestSampleTab);
this.hideRequestPayloadSample = argValueToBoolean(raw.hideRequestPayloadSample);
this.menuToggle = argValueToBoolean(raw.menuToggle, true);
this.jsonSampleExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
raw.jsonSampleExpandLevel,
this.jsonSamplesExpandLevel = RedocNormalizedOptions.normalizeJsonSampleExpandLevel(
raw.jsonSamplesExpandLevel || raw.jsonSampleExpandLevel,
);
this.enumSkipQuotes = argValueToBoolean(raw.enumSkipQuotes);
this.hideSchemaTitles = argValueToBoolean(raw.hideSchemaTitles);
this.simpleOneOfTypeLabel = argValueToBoolean(raw.simpleOneOfTypeLabel);
this.payloadSampleIdx = RedocNormalizedOptions.normalizePayloadSampleIdx(raw.payloadSampleIdx);
this.expandSingleSchemaField = argValueToBoolean(raw.expandSingleSchemaField);
this.schemaExpansionLevel = argValueToExpandLevel(raw.schemaExpansionLevel);
this.schemasExpansionLevel = argValueToExpandLevel(
raw.schemasExpansionLevel || raw.schemaExpansionLevel,
);
this.showObjectSchemaExamples = argValueToBoolean(raw.showObjectSchemaExamples);
this.showSecuritySchemeType = argValueToBoolean(raw.showSecuritySchemeType);
this.hideSecuritySection = argValueToBoolean(raw.hideSecuritySection);
Expand Down
9 changes: 8 additions & 1 deletion src/services/__tests__/models/ApiInfo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('Models', () => {
} as any;

const opts = new RedocNormalizedOptions({
downloadDefinitionUrl: 'https:test.com/filename.yaml',
downloadUrls: [{ title: 'Openapi description', url: 'https:test.com/filename.yaml' }],
});
const info = new ApiInfoModel(parser, opts);
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
Expand All @@ -160,6 +160,13 @@ describe('Models', () => {
const info = new ApiInfoModel(parser, opts);
expect(info.downloadLink).toEqual('https:test.com/filename.yaml');
expect(info.downloadFileName).toEqual('test.yaml');

const opts2 = new RedocNormalizedOptions({
downloadUrls: [{ title: 'test.yaml', url: 'https:test.com/filename.yaml' }],
});
const info2 = new ApiInfoModel(parser, opts2);
expect(info2.downloadLink).toEqual('https:test.com/filename.yaml');
expect(info2.downloadFileName).toEqual('test.yaml');
});
});
});
29 changes: 14 additions & 15 deletions src/services/models/ApiInfo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { OpenAPIContact, OpenAPIInfo, OpenAPILicense } from '../../types';
import { IS_BROWSER } from '../../utils/';
import type { OpenAPIParser } from '../OpenAPIParser';
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import { DownloadUrlsConfig, RedocNormalizedOptions } from '../RedocNormalizedOptions';

export class ApiInfoModel implements OpenAPIInfo {
title: string;
Expand All @@ -13,8 +13,7 @@ export class ApiInfoModel implements OpenAPIInfo {
contact?: OpenAPIContact;
license?: OpenAPILicense;

downloadLink?: string;
downloadFileName?: string;
downloadUrls?: DownloadUrlsConfig;

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

this.downloadLink = this.getDownloadLink();
this.downloadFileName = this.getDownloadFileName();
this.downloadUrls = this.getDownloadUrls();
}
private getDownloadUrls(): DownloadUrlsConfig | undefined {
return this.options.downloadUrls
?.map(({ title, url }) => ({
title: title || 'openapi.json',
url: this.getDownloadLink(url) || '',
}))
.filter(({ title, url }) => title && url);
}

private getDownloadLink(): string | undefined {
if (this.options.downloadDefinitionUrl) {
return this.options.downloadDefinitionUrl;
private getDownloadLink(url?: string): string | undefined {
if (url) {
return url;
}

if (this.parser.specUrl) {
Expand All @@ -49,11 +55,4 @@ export class ApiInfoModel implements OpenAPIInfo {
return window.URL.createObjectURL(blob);
}
}

private getDownloadFileName(): string | undefined {
if (!this.parser.specUrl && !this.options.downloadDefinitionUrl) {
return this.options.downloadFileName || 'openapi.json';
}
return this.options.downloadFileName;
}
}
2 changes: 1 addition & 1 deletion src/services/models/Operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export class OperationModel implements IMenuItem {
if (this.options.sortPropsAlphabetically) {
return sortByField(_parameters, 'name');
}
if (this.options.requiredPropsFirst) {
if (this.options.sortRequiredPropsFirst) {
return sortByRequired(_parameters);
}

Expand Down
2 changes: 1 addition & 1 deletion src/services/models/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ function buildFields(
if (options.sortPropsAlphabetically) {
fields = sortByField(fields, 'name');
}
if (options.requiredPropsFirst) {
if (options.sortRequiredPropsFirst) {
// if not sort alphabetically sort in the order from required keyword
fields = sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
}
Expand Down

0 comments on commit 30db4df

Please sign in to comment.