Skip to content

Commit

Permalink
feat: initial display security requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanHotsiy committed Dec 15, 2017
1 parent 9be4071 commit 50e2a58
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/common-elements/fields-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 & {
Expand Down
4 changes: 3 additions & 1 deletion src/components/Operation/Operation.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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>
Expand Down
72 changes: 72 additions & 0 deletions src/components/SecurityRequirement/SecuirityRequirement.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
}
9 changes: 6 additions & 3 deletions src/components/SecuritySchemes/SecuritySchemes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion src/services/OpenAPIParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] };

Expand Down
14 changes: 10 additions & 4 deletions src/services/models/Operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
*/
Expand Down Expand Up @@ -50,6 +47,7 @@ export class OperationModel implements IMenuItem {
responses: ResponseModel[];
path: string;
servers: OpenAPIServer[];
security: SecurityRequirementModel[];
codeSamples: CodeSample[];

constructor(
Expand Down Expand Up @@ -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),
);
}

/**
Expand All @@ -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 [
Expand Down
27 changes: 27 additions & 0 deletions src/services/models/SecurityRequirement.ts
Original file line number Diff line number Diff line change
@@ -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,
};
});
}
}
5 changes: 4 additions & 1 deletion src/services/models/SecuritySchemes.ts
Original file line number Diff line number Diff line change
@@ -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?: {
Expand All @@ -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') {
Expand Down Expand Up @@ -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]),
Expand Down
3 changes: 2 additions & 1 deletion src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const theme = {
},
schemaView: {
linesColor: '#7f99cf',
defaultDetailsWidth: '75%',
},
baseFont: {
size: '14px',
Expand All @@ -35,7 +36,7 @@ const theme = {
},
code: {
fontSize: '13px',
fontFamily: '"Lucida Console", Monaco, monospace',
fontFamily: 'Courirer, monospace',
},
links: {
color: undefined, // by default main color
Expand Down
4 changes: 3 additions & 1 deletion src/types/open-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 2 additions & 0 deletions src/utils/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,5 @@ export function humanizeConstraints(schema: OpenAPISchema): string[] {

return res;
}

export const SECURITY_SCHEMES_SECTION = 'section/Authentication/';

0 comments on commit 50e2a58

Please sign in to comment.