mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-10-30 23:37:28 +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