mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-23 00:56:33 +03:00
feat: initial display security requirements
This commit is contained in:
parent
9be40718f4
commit
50e2a5868d
|
@ -73,7 +73,7 @@ export const PropertyNameCell = PropertyCell.extend`
|
||||||
export const PropertyDetailsCell = styled.td`
|
export const PropertyDetailsCell = styled.td`
|
||||||
border-bottom: 1px solid #9fb4be;
|
border-bottom: 1px solid #9fb4be;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
width: 75%;
|
width: ${props => props.theme.schemaView.defaultDetailsWidth};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
tr.expanded & {
|
tr.expanded & {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
|
import { SecurityRequirements } from '../SecurityRequirement/SecuirityRequirement';
|
||||||
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
import { H2, MiddlePanel, DarkRightPanel, Badge, Row } from '../../common-elements';
|
import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elements';
|
||||||
|
|
||||||
import { ComponentWithOptions } from '../OptionsProvider';
|
import { ComponentWithOptions } from '../OptionsProvider';
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ export class Operation extends ComponentWithOptions<OperationProps> {
|
||||||
</H2>
|
</H2>
|
||||||
{pathInMiddle && <Endpoint operation={operation} inverted={true} />}
|
{pathInMiddle && <Endpoint operation={operation} inverted={true} />}
|
||||||
{description !== undefined && <Markdown source={description} />}
|
{description !== undefined && <Markdown source={description} />}
|
||||||
|
<SecurityRequirements securities={operation.security} />
|
||||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||||
<ResponsesList responses={operation.responses} />
|
<ResponsesList responses={operation.responses} />
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
|
|
72
src/components/SecurityRequirement/SecuirityRequirement.tsx
Normal file
72
src/components/SecurityRequirement/SecuirityRequirement.tsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
import { transparentizeHex } from '../../utils/styled';
|
||||||
|
|
||||||
|
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
|
||||||
|
import { UnderlinedHeader } from '../../common-elements/headers';
|
||||||
|
|
||||||
|
const ScopeName = styled.code`
|
||||||
|
font-size: ${props => props.theme.code.fontSize};
|
||||||
|
font-family: ${props => props.theme.code.fontFamily};
|
||||||
|
border: 1px solid ${props => transparentizeHex(props.theme.colors.text, 0.15)};
|
||||||
|
margin: 0 3px;
|
||||||
|
padding: 0.2em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export interface SecurityRequirementProps {
|
||||||
|
security: SecurityRequirementModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SecurityRequirement extends React.PureComponent<SecurityRequirementProps> {
|
||||||
|
render() {
|
||||||
|
const security = this.props.security;
|
||||||
|
return security.schemes.map((scheme, idx) => {
|
||||||
|
return (
|
||||||
|
<div key={scheme.id}>
|
||||||
|
<a href={'#' + scheme.sectionId}>{scheme.id}</a>
|
||||||
|
{scheme.scopes.length > 0 && ' ('}
|
||||||
|
{scheme.scopes.map(scope => <ScopeName key={scope}>{scope}</ScopeName>)}
|
||||||
|
{scheme.scopes.length > 0 && ') '}
|
||||||
|
{idx < security.schemes.length - 1 && ' and '}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthHeaderColumn = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - ${props => props.theme.schemaView.defaultDetailsWidth});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SecuritiesColumn = styled.div`
|
||||||
|
width: ${props => props.theme.schemaView.defaultDetailsWidth};
|
||||||
|
display: inline-block;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const AuthHeader = styled(UnderlinedHeader)`
|
||||||
|
display: inline-block;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export interface SecurityRequirementsProps {
|
||||||
|
securities: SecurityRequirementModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SecurityRequirements extends React.PureComponent<SecurityRequirementsProps> {
|
||||||
|
render() {
|
||||||
|
const securities = this.props.securities;
|
||||||
|
if (!securities.length) return null;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AuthHeaderColumn>
|
||||||
|
<AuthHeader>Authorizations: </AuthHeader>
|
||||||
|
</AuthHeaderColumn>
|
||||||
|
<SecuritiesColumn>
|
||||||
|
{securities.map((security, idx) => <SecurityRequirement key={idx} security={security} />)}
|
||||||
|
</SecuritiesColumn>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
||||||
import { SecuritySchemesModel } from '../../services/models';
|
import { SecuritySchemesModel } from '../../services/models';
|
||||||
|
|
||||||
import styled from '../../styled-components';
|
import styled from '../../styled-components';
|
||||||
import { H2 } from '../../common-elements';
|
import { H2, ShareLink } from '../../common-elements';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { OpenAPISecurityScheme } from '../../types';
|
import { OpenAPISecurityScheme } from '../../types';
|
||||||
|
|
||||||
|
@ -81,8 +81,11 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.props.securitySchemes.schemes.map(scheme => (
|
{this.props.securitySchemes.schemes.map(scheme => (
|
||||||
<div key={scheme.id}>
|
<div data-section-id={scheme.sectionId} key={scheme.id}>
|
||||||
<H2>{scheme.id}</H2>
|
<H2>
|
||||||
|
<ShareLink href={'#' + scheme.sectionId} />
|
||||||
|
{scheme.id}
|
||||||
|
</H2>
|
||||||
<Markdown source={scheme.description || ''} />
|
<Markdown source={scheme.description || ''} />
|
||||||
<AuthTable className="security-details">
|
<AuthTable className="security-details">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { JsonPointer } from '../utils/JsonPointer';
|
||||||
import { isNamedDefinition } from '../utils/openapi';
|
import { isNamedDefinition } from '../utils/openapi';
|
||||||
import { COMPONENT_REGEXP, buildComponentComment } from './MarkdownRenderer';
|
import { COMPONENT_REGEXP, buildComponentComment } from './MarkdownRenderer';
|
||||||
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
||||||
import { appendToMdHeading } from '../utils/index';
|
import { appendToMdHeading } from '../utils/';
|
||||||
|
|
||||||
export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
|
export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { join as joinPaths } from 'path';
|
||||||
import { parse as urlParse } from 'url';
|
import { parse as urlParse } from 'url';
|
||||||
|
|
||||||
import { IMenuItem } from '../MenuStore';
|
import { IMenuItem } from '../MenuStore';
|
||||||
|
import { SecurityRequirementModel } from './SecurityRequirement';
|
||||||
import { GroupModel } from './Group.model';
|
import { GroupModel } from './Group.model';
|
||||||
|
|
||||||
import { OpenAPIExternalDocumentation, OpenAPIServer } from '../../types';
|
import { OpenAPIExternalDocumentation, OpenAPIServer } from '../../types';
|
||||||
|
@ -16,10 +17,6 @@ import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
|
||||||
import { JsonPointer, getOperationSummary, isAbsolutePath, stripTrailingSlash } from '../../utils';
|
import { JsonPointer, getOperationSummary, isAbsolutePath, stripTrailingSlash } from '../../utils';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
|
|
||||||
function isNumeric(n) {
|
|
||||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operation model ready to be used by components
|
* Operation model ready to be used by components
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +47,7 @@ export class OperationModel implements IMenuItem {
|
||||||
responses: ResponseModel[];
|
responses: ResponseModel[];
|
||||||
path: string;
|
path: string;
|
||||||
servers: OpenAPIServer[];
|
servers: OpenAPIServer[];
|
||||||
|
security: SecurityRequirementModel[];
|
||||||
codeSamples: CodeSample[];
|
codeSamples: CodeSample[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -101,6 +99,10 @@ export class OperationModel implements IMenuItem {
|
||||||
parser.specUrl,
|
parser.specUrl,
|
||||||
operationSpec.servers || parser.spec.servers || [],
|
operationSpec.servers || parser.spec.servers || [],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.security = (operationSpec.security || parser.spec.security || []).map(
|
||||||
|
security => new SecurityRequirementModel(security, parser),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,6 +128,10 @@ export class OperationModel implements IMenuItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNumeric(n) {
|
||||||
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeServers(specUrl: string, servers: OpenAPIServer[]): OpenAPIServer[] {
|
function normalizeServers(specUrl: string, servers: OpenAPIServer[]): OpenAPIServer[] {
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
return [
|
return [
|
||||||
|
|
27
src/services/models/SecurityRequirement.ts
Normal file
27
src/services/models/SecurityRequirement.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { OpenAPISecurityRequirement } from '../../types';
|
||||||
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
import { SECURITY_SCHEMES_SECTION } from '../../utils/openapi';
|
||||||
|
|
||||||
|
export class SecurityRequirementModel {
|
||||||
|
schemes: {
|
||||||
|
id: string;
|
||||||
|
sectionId: string;
|
||||||
|
type: string;
|
||||||
|
scopes: string[];
|
||||||
|
}[];
|
||||||
|
|
||||||
|
constructor(requirement: OpenAPISecurityRequirement, parser: OpenAPIParser) {
|
||||||
|
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
||||||
|
|
||||||
|
this.schemes = Object.keys(requirement || {}).map(id => {
|
||||||
|
const scheme = parser.deref(schemes[id]);
|
||||||
|
const scopes = requirement[id] || [];
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
sectionId: SECURITY_SCHEMES_SECTION + id,
|
||||||
|
type: scheme.type,
|
||||||
|
scopes,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
import { OpenAPISecurityScheme, Referenced } from '../../types';
|
import { OpenAPISecurityScheme, Referenced } from '../../types';
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import { OpenAPIParser } from '../OpenAPIParser';
|
||||||
|
import { SECURITY_SCHEMES_SECTION } from '../../utils/openapi';
|
||||||
|
|
||||||
export class SecuritySchemeModel {
|
export class SecuritySchemeModel {
|
||||||
id: string;
|
id: string;
|
||||||
|
sectionId: string;
|
||||||
type: OpenAPISecurityScheme['type'];
|
type: OpenAPISecurityScheme['type'];
|
||||||
description: string;
|
description: string;
|
||||||
apiKey?: {
|
apiKey?: {
|
||||||
|
@ -23,6 +25,7 @@ export class SecuritySchemeModel {
|
||||||
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
|
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
|
||||||
const info = parser.deref(scheme);
|
const info = parser.deref(scheme);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.sectionId = SECURITY_SCHEMES_SECTION + id;
|
||||||
this.type = info.type;
|
this.type = info.type;
|
||||||
this.description = info.description || '';
|
this.description = info.description || '';
|
||||||
if (info.type === 'apiKey') {
|
if (info.type === 'apiKey') {
|
||||||
|
@ -54,7 +57,7 @@ export class SecuritySchemeModel {
|
||||||
export class SecuritySchemesModel {
|
export class SecuritySchemesModel {
|
||||||
schemes: SecuritySchemeModel[];
|
schemes: SecuritySchemeModel[];
|
||||||
|
|
||||||
constructor(public parser: OpenAPIParser) {
|
constructor(parser: OpenAPIParser) {
|
||||||
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
||||||
this.schemes = Object.keys(schemes).map(
|
this.schemes = Object.keys(schemes).map(
|
||||||
name => new SecuritySchemeModel(parser, name, schemes[name]),
|
name => new SecuritySchemeModel(parser, name, schemes[name]),
|
||||||
|
|
|
@ -21,6 +21,7 @@ const theme = {
|
||||||
},
|
},
|
||||||
schemaView: {
|
schemaView: {
|
||||||
linesColor: '#7f99cf',
|
linesColor: '#7f99cf',
|
||||||
|
defaultDetailsWidth: '75%',
|
||||||
},
|
},
|
||||||
baseFont: {
|
baseFont: {
|
||||||
size: '14px',
|
size: '14px',
|
||||||
|
@ -35,7 +36,7 @@ const theme = {
|
||||||
},
|
},
|
||||||
code: {
|
code: {
|
||||||
fontSize: '13px',
|
fontSize: '13px',
|
||||||
fontFamily: '"Lucida Console", Monaco, monospace',
|
fontFamily: 'Courirer, monospace',
|
||||||
},
|
},
|
||||||
links: {
|
links: {
|
||||||
color: undefined, // by default main color
|
color: undefined, // by default main color
|
||||||
|
|
|
@ -200,7 +200,9 @@ export type OpenAPIComponents = {
|
||||||
callbacks?: { [name: string]: Referenced<OpenAPICallback> };
|
callbacks?: { [name: string]: Referenced<OpenAPICallback> };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OpenAPISecurityRequirement = {};
|
export type OpenAPISecurityRequirement = {
|
||||||
|
[name: string]: string[];
|
||||||
|
};
|
||||||
|
|
||||||
export type OpenAPISecurityScheme = {
|
export type OpenAPISecurityScheme = {
|
||||||
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
|
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
|
||||||
|
|
|
@ -161,3 +161,5 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SECURITY_SCHEMES_SECTION = 'section/Authentication/';
|
||||||
|
|
Loading…
Reference in New Issue
Block a user