feat: ConsoleViewer can send the proper request customized by user now

This commit is contained in:
Arian Rahimi 2020-02-09 18:04:30 +03:30
parent e80fe1d47c
commit 2a927eb27c
7 changed files with 68 additions and 80 deletions

View File

@ -1,8 +1,8 @@
openapi: 3.0.0
servers:
- url: //petstore.swagger.io/v2
- url: https://petstore.swagger.io/v2
description: Default server
- url: //petstore.swagger.io/sandbox
- url: https://petstore.swagger.io/sandbox
description: Sandbox server
info:
description: |
@ -101,7 +101,7 @@ paths:
'405':
description: Invalid input
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
x-code-samples:
@ -149,7 +149,7 @@ paths:
'405':
description: Validation exception
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
x-code-samples:
@ -217,7 +217,7 @@ paths:
'405':
description: Invalid input
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
requestBody:
@ -256,7 +256,7 @@ paths:
'400':
description: Invalid pet value
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
'/pet/{petId}/uploadImage':
@ -282,7 +282,7 @@ paths:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
requestBody:
@ -332,7 +332,7 @@ paths:
'400':
description: Invalid status value
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
/pet/findByTags:
@ -372,7 +372,7 @@ paths:
'400':
description: Invalid tag value
security:
- petstore_auth:
- authorization:
- 'write:pets'
- 'read:pets'
/store/inventory:
@ -923,7 +923,7 @@ components:
description: List of user object
required: true
securitySchemes:
petstore_auth:
authorization:
description: |
Get access to data while protecting your account credentials.
OAuth2 is also a safer and more secure way to give you access.

View File

@ -25,9 +25,7 @@ const specUrl =
(userUrl && userUrl[1]) || (swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml');
let store;
const headers = {
'x-nutanix-client': 'ui',
};
const headers = {};
const options: RedocRawOptions = { nativeScrollbars: false, enableConsole: true, providedByName: 'Intent ApiDocs by Nutanix', providedByUri: 'http://www.nutanix.com', additionalHeaders: headers };
async function init() {

View File

@ -1,9 +1,9 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { SecuritySchemeModel } from '../../../typings/services/models';
import { SubmitButton } from '../../common-elements/buttons';
import { FlexLayoutReverse } from '../../common-elements/panels';
import { FieldModel, OperationModel } from '../../services/models';
import { OpenAPISchema } from '../../types';
import { FieldModel, OperationModel, SecuritySchemesModel } from '../../services/models';
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
import { ConsoleEditor } from './ConsoleEditor';
@ -14,6 +14,7 @@ export interface ConsoleViewerProps {
additionalHeaders?: object;
queryParamPrefix?: string;
queryParamSuffix?: string;
securitySchemes: SecuritySchemesModel;
}
export interface ConsoleViewerState {
@ -22,7 +23,6 @@ export interface ConsoleViewerState {
export interface Schema {
_$ref?: any;
rawSchema?: OpenAPISchema;
}
@observer
@ -40,7 +40,8 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
}
onClickSend = async () => {
const ace = this.consoleEditor && this.consoleEditor.editor;
const { operation, additionalHeaders = {} } = this.props;
const { operation, securitySchemes: {schemes}, additionalHeaders = {} } = this.props;
let value = ace && ace.editor.getValue();
const content = operation.requestBody && operation.requestBody.content;
@ -54,7 +55,23 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
}
const contentType = mediaType && mediaType.name || 'application/json';
const contentTypeHeader = { 'Content-Type': contentType };
const headers = { ...additionalHeaders, ...contentTypeHeader };
const schemeMapper: Map<string, SecuritySchemeModel> = new Map<string, SecuritySchemeModel>();
schemes.forEach(scheme => {
schemeMapper.set(scheme.id, scheme);
});
const securityHeaders: Dict<string | undefined> = {};
operation.security.forEach(({schemes: [{ id }]}) => {
if (schemeMapper.has(id)) {
// this part of code needs a ts-ignore because typescript couldn't detect that schemeMapper.get(id) -
// has been checked to avoid token of undefined.
// @ts-ignore
securityHeaders[id] = schemeMapper.get(id).token;
}
});
const headers = { ...additionalHeaders, ...contentTypeHeader, ...securityHeaders };
let result;
try {
result = await this.invoke(endpoint, value, headers);
@ -76,8 +93,6 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
const queryParamSuffix = '}';
for (const fieldModel of params) {
console.log(fieldModel.name + ' ' + url);
console.log(fieldModel.$value);
if (url.indexOf(`${queryParamPrefix}${fieldModel.name}${queryParamSuffix}`) > -1 && fieldModel.$value.length > 0) {
url = url.replace(`${queryParamPrefix}${fieldModel.name}${queryParamSuffix}`, fieldModel.$value);
}
@ -106,7 +121,6 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
const request = new Request(url, {
method: endpoint.method,
credentials: 'include',
redirect: 'manual',
headers: myHeaders,
body: (body) ? JSON.stringify(body) : undefined,
@ -173,29 +187,4 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
</div>
);
}
getSchema() {
const { operation } = this.props;
const requestBodyContent = operation.requestBody && operation.requestBody.content && operation.requestBody.content;
const mediaTypes = (requestBodyContent && requestBodyContent.mediaTypes) ? requestBodyContent.mediaTypes : [];
if (!mediaTypes.length) {
return null;
}
const schema: Schema = {
};
for (const mediaType of mediaTypes) {
if (mediaType.name.indexOf('json') > -1) {
if (mediaType.schema) {
schema.rawSchema = mediaType.schema && mediaType.schema.rawSchema;
console.log('rawSchema : ' + JSON.stringify(schema));
console.log('schema : ' + JSON.stringify(mediaType.schema.schema));
}
break;
}
}
return schema;
}
}

View File

@ -1,35 +1,40 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { AppStore } from '../../services';
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
import { ContentItemModel } from '../../services/MenuBuilder';
import { GroupModel, OperationModel } from '../../services/models';
import { GroupModel } from '../../services/models';
import { Operation } from '../Operation/Operation';
@observer
export class ContentItems extends React.Component<{
export interface ContentItemsProps {
items: ContentItemModel[];
}> {
store: AppStore;
}
@observer
export class ContentItems extends React.Component<ContentItemsProps> {
render() {
const items = this.props.items;
const { items, store } = this.props;
if (items.length === 0) {
return null;
}
return items.map(item => <ContentItem item={item} key={item.id} />);
return items.map(item => <ContentItem store={store} item={item} key={item.id} />);
}
}
export interface ContentItemProps {
item: ContentItemModel;
store: AppStore;
}
@observer
export class ContentItem extends React.Component<ContentItemProps> {
render() {
const item = this.props.item;
const { item, store } = this.props;
let content;
const { type } = item;
switch (type) {
@ -41,7 +46,7 @@ export class ContentItem extends React.Component<ContentItemProps> {
content = <SectionItem {...this.props} />;
break;
case 'operation':
content = <OperationItem item={item as any} />;
content = <Operation securitySchemes={store.spec.securitySchemes} operation={item as any} />;
break;
default:
content = <SectionItem {...this.props} />;
@ -54,7 +59,7 @@ export class ContentItem extends React.Component<ContentItemProps> {
{content}
</Section>
)}
{item.items && <ContentItems items={item.items} />}
{item.items && <ContentItems store={store} items={item.items} />}
</>
);
}
@ -90,12 +95,3 @@ export class SectionItem extends React.Component<ContentItemProps> {
);
}
}
@observer
export class OperationItem extends React.Component<{
item: OperationModel;
}> {
render() {
return <Operation operation={this.props.item} />;
}
}

View File

@ -5,7 +5,7 @@ import { Badge, DarkRightPanel, H2, MiddlePanel, Row } from '../../common-elemen
import { ShareLink } from '../../common-elements/linkify';
import { OperationModel as OperationType } from '../../services/models';
import { OperationModel as OperationType, SecuritySchemesModel } from '../../services/models';
import styled from '../../styled-components';
import { ConsoleViewer } from '../Console/ConsoleViewer';
import { Endpoint } from '../Endpoint/Endpoint';
@ -34,6 +34,7 @@ const Description = styled.div`
export interface OperationProps {
operation: OperationType;
securitySchemes: SecuritySchemesModel;
}
export interface OperationState {
@ -57,7 +58,7 @@ export class Operation extends React.Component<OperationProps, OperationState> {
}
render() {
const { operation } = this.props;
const { operation, securitySchemes } = this.props;
const { executeMode } = this.state;
const { name: summary, description, deprecated, externalDocs } = operation;
@ -95,7 +96,13 @@ export class Operation extends React.Component<OperationProps, OperationState> {
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
{executeMode &&
<div>
<ConsoleViewer operation={operation} additionalHeaders={options.additionalHeaders} queryParamPrefix={options.queryParamPrefix} queryParamSuffix={options.queryParamSuffix} />
<ConsoleViewer
securitySchemes={securitySchemes}
operation={operation}
additionalHeaders={options.additionalHeaders}
queryParamPrefix={options.queryParamPrefix}
queryParamSuffix={options.queryParamSuffix}
/>
</div>
}
{!executeMode &&

View File

@ -57,7 +57,7 @@ export class Redoc extends React.Component<RedocProps> {
</StickyResponsiveSidebar>
<ApiContentWrap className="api-content">
<ApiInfo store={store} />
<ContentItems items={menu.items as any} />
<ContentItems store={store} items={menu.items as any} />
</ApiContentWrap>
<BackgroundStub />
</RedocWrap>

View File

@ -67,8 +67,7 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
const security = this.props.security;
return (
<SecurityRequirementOrWrap>
{security.schemes.map(scheme => {
return (
{security.schemes.map(scheme => (
<SecurityRequirementAndWrap key={scheme.id}>
<Link to={scheme.sectionId}>{scheme.id}</Link>
{scheme.scopes.length > 0 && ' ('}
@ -77,8 +76,7 @@ export class SecurityRequirement extends React.PureComponent<SecurityRequirement
))}
{scheme.scopes.length > 0 && ') '}
</SecurityRequirementAndWrap>
);
})}
))}
</SecurityRequirementOrWrap>
);
}