mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-22 16:46:34 +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`
|
||||
border-bottom: 1px solid #9fb4be;
|
||||
padding: 10px 0;
|
||||
width: 75%;
|
||||
width: ${props => props.theme.schemaView.defaultDetailsWidth};
|
||||
box-sizing: border-box;
|
||||
|
||||
tr.expanded & {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import * as React from 'react';
|
||||
import styled from '../../styled-components';
|
||||
import { SecurityRequirements } from '../SecurityRequirement/SecuirityRequirement';
|
||||
|
||||
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';
|
||||
|
||||
|
@ -53,6 +54,7 @@ export class Operation extends ComponentWithOptions<OperationProps> {
|
|||
</H2>
|
||||
{pathInMiddle && <Endpoint operation={operation} inverted={true} />}
|
||||
{description !== undefined && <Markdown source={description} />}
|
||||
<SecurityRequirements securities={operation.security} />
|
||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||
<ResponsesList responses={operation.responses} />
|
||||
</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 styled from '../../styled-components';
|
||||
import { H2 } from '../../common-elements';
|
||||
import { H2, ShareLink } from '../../common-elements';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { OpenAPISecurityScheme } from '../../types';
|
||||
|
||||
|
@ -81,8 +81,11 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
|||
return (
|
||||
<div>
|
||||
{this.props.securitySchemes.schemes.map(scheme => (
|
||||
<div key={scheme.id}>
|
||||
<H2>{scheme.id}</H2>
|
||||
<div data-section-id={scheme.sectionId} key={scheme.id}>
|
||||
<H2>
|
||||
<ShareLink href={'#' + scheme.sectionId} />
|
||||
{scheme.id}
|
||||
</H2>
|
||||
<Markdown source={scheme.description || ''} />
|
||||
<AuthTable className="security-details">
|
||||
<tbody>
|
||||
|
|
|
@ -7,7 +7,7 @@ import { JsonPointer } from '../utils/JsonPointer';
|
|||
import { isNamedDefinition } from '../utils/openapi';
|
||||
import { COMPONENT_REGEXP, buildComponentComment } from './MarkdownRenderer';
|
||||
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
|
||||
import { appendToMdHeading } from '../utils/index';
|
||||
import { appendToMdHeading } from '../utils/';
|
||||
|
||||
export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { join as joinPaths } from 'path';
|
|||
import { parse as urlParse } from 'url';
|
||||
|
||||
import { IMenuItem } from '../MenuStore';
|
||||
import { SecurityRequirementModel } from './SecurityRequirement';
|
||||
import { GroupModel } from './Group.model';
|
||||
|
||||
import { OpenAPIExternalDocumentation, OpenAPIServer } from '../../types';
|
||||
|
@ -16,10 +17,6 @@ import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
|
|||
import { JsonPointer, getOperationSummary, isAbsolutePath, stripTrailingSlash } from '../../utils';
|
||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||
|
||||
function isNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Operation model ready to be used by components
|
||||
*/
|
||||
|
@ -50,6 +47,7 @@ export class OperationModel implements IMenuItem {
|
|||
responses: ResponseModel[];
|
||||
path: string;
|
||||
servers: OpenAPIServer[];
|
||||
security: SecurityRequirementModel[];
|
||||
codeSamples: CodeSample[];
|
||||
|
||||
constructor(
|
||||
|
@ -101,6 +99,10 @@ export class OperationModel implements IMenuItem {
|
|||
parser.specUrl,
|
||||
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[] {
|
||||
if (servers.length === 0) {
|
||||
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 { OpenAPIParser } from '../OpenAPIParser';
|
||||
import { SECURITY_SCHEMES_SECTION } from '../../utils/openapi';
|
||||
|
||||
export class SecuritySchemeModel {
|
||||
id: string;
|
||||
sectionId: string;
|
||||
type: OpenAPISecurityScheme['type'];
|
||||
description: string;
|
||||
apiKey?: {
|
||||
|
@ -23,6 +25,7 @@ export class SecuritySchemeModel {
|
|||
constructor(parser: OpenAPIParser, id: string, scheme: Referenced<OpenAPISecurityScheme>) {
|
||||
const info = parser.deref(scheme);
|
||||
this.id = id;
|
||||
this.sectionId = SECURITY_SCHEMES_SECTION + id;
|
||||
this.type = info.type;
|
||||
this.description = info.description || '';
|
||||
if (info.type === 'apiKey') {
|
||||
|
@ -54,7 +57,7 @@ export class SecuritySchemeModel {
|
|||
export class SecuritySchemesModel {
|
||||
schemes: SecuritySchemeModel[];
|
||||
|
||||
constructor(public parser: OpenAPIParser) {
|
||||
constructor(parser: OpenAPIParser) {
|
||||
const schemes = (parser.spec.components && parser.spec.components.securitySchemes) || {};
|
||||
this.schemes = Object.keys(schemes).map(
|
||||
name => new SecuritySchemeModel(parser, name, schemes[name]),
|
||||
|
|
|
@ -21,6 +21,7 @@ const theme = {
|
|||
},
|
||||
schemaView: {
|
||||
linesColor: '#7f99cf',
|
||||
defaultDetailsWidth: '75%',
|
||||
},
|
||||
baseFont: {
|
||||
size: '14px',
|
||||
|
@ -35,7 +36,7 @@ const theme = {
|
|||
},
|
||||
code: {
|
||||
fontSize: '13px',
|
||||
fontFamily: '"Lucida Console", Monaco, monospace',
|
||||
fontFamily: 'Courirer, monospace',
|
||||
},
|
||||
links: {
|
||||
color: undefined, // by default main color
|
||||
|
|
|
@ -200,7 +200,9 @@ export type OpenAPIComponents = {
|
|||
callbacks?: { [name: string]: Referenced<OpenAPICallback> };
|
||||
};
|
||||
|
||||
export type OpenAPISecurityRequirement = {};
|
||||
export type OpenAPISecurityRequirement = {
|
||||
[name: string]: string[];
|
||||
};
|
||||
|
||||
export type OpenAPISecurityScheme = {
|
||||
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
|
||||
|
|
|
@ -161,3 +161,5 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] {
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
export const SECURITY_SCHEMES_SECTION = 'section/Authentication/';
|
||||
|
|
Loading…
Reference in New Issue
Block a user