mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 01:37:32 +03:00 
			
		
		
		
	Implement expandResponses option
This commit is contained in:
		
							parent
							
								
									e85718225d
								
							
						
					
					
						commit
						f52a05a2b5
					
				| 
						 | 
					@ -6,6 +6,7 @@ import { AppContainer } from 'react-hot-loader';
 | 
				
			||||||
import { Redoc, RedocProps } from '../../src/components/Redoc/Redoc';
 | 
					import { Redoc, RedocProps } from '../../src/components/Redoc/Redoc';
 | 
				
			||||||
import { AppStore } from '../../src/services/AppStore';
 | 
					import { AppStore } from '../../src/services/AppStore';
 | 
				
			||||||
import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
 | 
					import { loadAndBundleSpec } from '../../src/utils/loadAndBundleSpec';
 | 
				
			||||||
 | 
					import { RedocRawOptions } from '../../src/services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const renderRoot = (Component: typeof Redoc, props: RedocProps) =>
 | 
					const renderRoot = (Component: typeof Redoc, props: RedocProps) =>
 | 
				
			||||||
  render(
 | 
					  render(
 | 
				
			||||||
| 
						 | 
					@ -23,12 +24,12 @@ const swagger = window.location.search.indexOf('swagger') > -1; //compatibility
 | 
				
			||||||
const specUrl = swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml';
 | 
					const specUrl = swagger ? 'swagger.yaml' : big ? 'big-openapi.json' : 'openapi.yaml';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let store;
 | 
					let store;
 | 
				
			||||||
const options = {};
 | 
					const options: RedocRawOptions = { expandResponses: 'all' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function init() {
 | 
					async function init() {
 | 
				
			||||||
  const spec = await loadAndBundleSpec(specUrl);
 | 
					  const spec = await loadAndBundleSpec(specUrl);
 | 
				
			||||||
  store = new AppStore(spec, specUrl);
 | 
					  store = new AppStore(spec, specUrl, options);
 | 
				
			||||||
  renderRoot(Redoc, { store: store, options });
 | 
					  renderRoot(Redoc, { store: store });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
init();
 | 
					init();
 | 
				
			||||||
| 
						 | 
					@ -43,7 +44,7 @@ if (module.hot) {
 | 
				
			||||||
      store = AppStore.fromJS(state);
 | 
					      store = AppStore.fromJS(state);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderRoot(Redoc, { store: store, options });
 | 
					    renderRoot(Redoc, { store: store });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  module.hot.accept(['../../src/components/Redoc/Redoc'], reload());
 | 
					  module.hot.accept(['../../src/components/Redoc/Redoc'], reload());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,10 @@
 | 
				
			||||||
import * as React from 'react';
 | 
					import * as React from 'react';
 | 
				
			||||||
import * as PropTypes from 'prop-types';
 | 
					import * as PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RedocNormalizedOptions, RedocRawOptions } from '../services/RedocNormalizedOptions';
 | 
					import { RedocNormalizedOptions } from '../services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface OptionsProviderProps {
 | 
					export interface OptionsProviderProps {
 | 
				
			||||||
  options: RedocRawOptions;
 | 
					  options: RedocNormalizedOptions;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class OptionsProvider extends React.Component<OptionsProviderProps> {
 | 
					export class OptionsProvider extends React.Component<OptionsProviderProps> {
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ export class OptionsProvider extends React.Component<OptionsProviderProps> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getChildContext() {
 | 
					  getChildContext() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      redocOptions: new RedocNormalizedOptions(this.props.options),
 | 
					      redocOptions: this.props.options,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,19 +11,14 @@ import { ContentItems } from '../ContentItems/ContentItems';
 | 
				
			||||||
import { AppStore } from '../../services';
 | 
					import { AppStore } from '../../services';
 | 
				
			||||||
import { OptionsProvider } from '../OptionsProvider';
 | 
					import { OptionsProvider } from '../OptionsProvider';
 | 
				
			||||||
import { StickySidebar } from '../StickySidebar/StickySidebar';
 | 
					import { StickySidebar } from '../StickySidebar/StickySidebar';
 | 
				
			||||||
import { RedocRawOptions } from '../../services/RedocNormalizedOptions';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import defaultTheme from '../../theme';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RedocProps {
 | 
					export interface RedocProps {
 | 
				
			||||||
  store: AppStore;
 | 
					  store: AppStore;
 | 
				
			||||||
  options?: RedocRawOptions;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Redoc extends React.Component<RedocProps> {
 | 
					export class Redoc extends React.Component<RedocProps> {
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    store: PropTypes.instanceOf(AppStore).isRequired,
 | 
					    store: PropTypes.instanceOf(AppStore).isRequired,
 | 
				
			||||||
    options: PropTypes.object,
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  componentDidMount() {
 | 
					  componentDidMount() {
 | 
				
			||||||
| 
						 | 
					@ -31,9 +26,9 @@ export class Redoc extends React.Component<RedocProps> {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { store: { spec, menu }, options = {} } = this.props;
 | 
					    const { store: { spec, menu, options } } = this.props;
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <ThemeProvider theme={{ ...options.theme, ...defaultTheme }}>
 | 
					      <ThemeProvider theme={options.theme}>
 | 
				
			||||||
        <OptionsProvider options={options}>
 | 
					        <OptionsProvider options={options}>
 | 
				
			||||||
          <RedocWrap className="redoc-wrap">
 | 
					          <RedocWrap className="redoc-wrap">
 | 
				
			||||||
            <StickySidebar className="menu-content">
 | 
					            <StickySidebar className="menu-content">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,18 +1,15 @@
 | 
				
			||||||
import * as React from 'react';
 | 
					import * as React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ThemeInterface } from '../theme';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { LoadingWrap } from './LoadingWrap/LoadingWrap';
 | 
					import { LoadingWrap } from './LoadingWrap/LoadingWrap';
 | 
				
			||||||
import { StoreProvider } from './StoreProvider';
 | 
					import { StoreProvider } from './StoreProvider';
 | 
				
			||||||
import { ErrorBoundary } from './ErrorBoundary';
 | 
					import { ErrorBoundary } from './ErrorBoundary';
 | 
				
			||||||
import { Redoc } from './Redoc/Redoc';
 | 
					import { Redoc } from './Redoc/Redoc';
 | 
				
			||||||
 | 
					import { RedocRawOptions } from '../services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RedocStandaloneProps {
 | 
					export interface RedocStandaloneProps {
 | 
				
			||||||
  spec?: object;
 | 
					  spec?: object;
 | 
				
			||||||
  specUrl?: string;
 | 
					  specUrl?: string;
 | 
				
			||||||
  options?: {
 | 
					  options?: RedocRawOptions;
 | 
				
			||||||
    theme?: ThemeInterface;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class RedocStandalone extends React.Component<RedocStandaloneProps> {
 | 
					export class RedocStandalone extends React.Component<RedocStandaloneProps> {
 | 
				
			||||||
| 
						 | 
					@ -41,10 +38,10 @@ export class RedocStandalone extends React.Component<RedocStandaloneProps> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <ErrorBoundary>
 | 
					      <ErrorBoundary>
 | 
				
			||||||
        <StoreProvider spec={spec} specUrl={specUrl}>
 | 
					        <StoreProvider spec={spec} specUrl={specUrl} options={options}>
 | 
				
			||||||
          {({ loading, store }) => (
 | 
					          {({ loading, store }) => (
 | 
				
			||||||
            <LoadingWrap loading={loading}>
 | 
					            <LoadingWrap loading={loading}>
 | 
				
			||||||
              <Redoc store={store} options={options} />
 | 
					              <Redoc store={store} />
 | 
				
			||||||
            </LoadingWrap>
 | 
					            </LoadingWrap>
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        </StoreProvider>
 | 
					        </StoreProvider>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ export class ResponseView extends React.Component<{ response: ResponseModel }> {
 | 
				
			||||||
          code={code}
 | 
					          code={code}
 | 
				
			||||||
          opened={expanded}
 | 
					          opened={expanded}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        {expanded && (
 | 
					        {expanded &&
 | 
				
			||||||
 | 
					          !empty && (
 | 
				
			||||||
            <ResponseDetailsWrap>
 | 
					            <ResponseDetailsWrap>
 | 
				
			||||||
              <ResponseHeaders headers={headers} />
 | 
					              <ResponseHeaders headers={headers} />
 | 
				
			||||||
              <MediaTypesSwitch
 | 
					              <MediaTypesSwitch
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,11 @@ const ResponsesHeader = styled.h3`
 | 
				
			||||||
  font-weight: normal;
 | 
					  font-weight: normal;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ResponsesList extends React.PureComponent<{
 | 
					export interface ResponseListProps {
 | 
				
			||||||
  responses: ResponseModel[];
 | 
					  responses: ResponseModel[];
 | 
				
			||||||
}> {
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ResponsesList extends React.PureComponent<ResponseListProps> {
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { responses } = this.props;
 | 
					    const { responses } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,12 +2,15 @@ import { Component } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { AppStore } from '../services/';
 | 
					import { AppStore } from '../services/';
 | 
				
			||||||
import { loadAndBundleSpec } from '../utils';
 | 
					import { loadAndBundleSpec } from '../utils';
 | 
				
			||||||
 | 
					import { RedocRawOptions } from '../services/RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface SpecProps {
 | 
					interface SpecProps {
 | 
				
			||||||
  specUrl?: string;
 | 
					  specUrl?: string;
 | 
				
			||||||
  spec?: object;
 | 
					  spec?: object;
 | 
				
			||||||
  store?: AppStore;
 | 
					  store?: AppStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  options?: RedocRawOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  children?: any;
 | 
					  children?: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +36,7 @@ export class StoreProvider extends Component<SpecProps, SpecState> {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async load() {
 | 
					  async load() {
 | 
				
			||||||
    let { specUrl, spec } = this.props;
 | 
					    let { specUrl, spec, options } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.setState({
 | 
					    this.setState({
 | 
				
			||||||
      loading: true,
 | 
					      loading: true,
 | 
				
			||||||
| 
						 | 
					@ -43,7 +46,7 @@ export class StoreProvider extends Component<SpecProps, SpecState> {
 | 
				
			||||||
      const resolvedSpec = await loadAndBundleSpec(spec || specUrl!);
 | 
					      const resolvedSpec = await loadAndBundleSpec(spec || specUrl!);
 | 
				
			||||||
      this.setState({
 | 
					      this.setState({
 | 
				
			||||||
        loading: false,
 | 
					        loading: false,
 | 
				
			||||||
        store: new AppStore(resolvedSpec, specUrl),
 | 
					        store: new AppStore(resolvedSpec, specUrl, options),
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      this.setState({
 | 
					      this.setState({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { SpecStore } from './models';
 | 
				
			||||||
import { MenuStore } from './MenuStore';
 | 
					import { MenuStore } from './MenuStore';
 | 
				
			||||||
import { ScrollService } from './ScrollService';
 | 
					import { ScrollService } from './ScrollService';
 | 
				
			||||||
import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
 | 
					import { loadAndBundleSpec } from '../utils/loadAndBundleSpec';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions, RedocRawOptions } from './RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type StoreData = {
 | 
					type StoreData = {
 | 
				
			||||||
  menu: {
 | 
					  menu: {
 | 
				
			||||||
| 
						 | 
					@ -12,22 +13,31 @@ type StoreData = {
 | 
				
			||||||
    url: string;
 | 
					    url: string;
 | 
				
			||||||
    data: any;
 | 
					    data: any;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					  options: RedocRawOptions;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function createStore(spec: object, specUrl: string) {
 | 
					export async function createStore(
 | 
				
			||||||
 | 
					  spec: object,
 | 
				
			||||||
 | 
					  specUrl: string | undefined,
 | 
				
			||||||
 | 
					  options: RedocRawOptions = {},
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
  const resolvedSpec = await loadAndBundleSpec(spec || specUrl);
 | 
					  const resolvedSpec = await loadAndBundleSpec(spec || specUrl);
 | 
				
			||||||
  return new AppStore(resolvedSpec, specUrl);
 | 
					  return new AppStore(resolvedSpec, specUrl, options);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AppStore {
 | 
					export class AppStore {
 | 
				
			||||||
  menu: MenuStore;
 | 
					  menu: MenuStore;
 | 
				
			||||||
  spec: SpecStore;
 | 
					  spec: SpecStore;
 | 
				
			||||||
 | 
					  rawOptions: RedocRawOptions;
 | 
				
			||||||
 | 
					  options: RedocNormalizedOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private scroll: ScrollService;
 | 
					  private scroll: ScrollService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(spec: OpenAPISpec, specUrl?: string) {
 | 
					  constructor(spec: OpenAPISpec, specUrl?: string, options: RedocRawOptions = {}) {
 | 
				
			||||||
 | 
					    this.rawOptions = options;
 | 
				
			||||||
 | 
					    this.options = new RedocNormalizedOptions(options);
 | 
				
			||||||
    this.scroll = new ScrollService();
 | 
					    this.scroll = new ScrollService();
 | 
				
			||||||
    this.spec = new SpecStore(spec, specUrl);
 | 
					    this.spec = new SpecStore(spec, specUrl, this.options);
 | 
				
			||||||
    this.menu = new MenuStore(this.spec, this.scroll);
 | 
					    this.menu = new MenuStore(this.spec, this.scroll);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,6 +60,7 @@ export class AppStore {
 | 
				
			||||||
        url: this.spec.parser.specUrl,
 | 
					        url: this.spec.parser.specUrl,
 | 
				
			||||||
        data: this.spec.parser.spec,
 | 
					        data: this.spec.parser.spec,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      options: this.rawOptions,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
| 
						 | 
					@ -58,7 +69,7 @@ export class AppStore {
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  // TODO:
 | 
					  // TODO:
 | 
				
			||||||
  static fromJS(state: StoreData): AppStore {
 | 
					  static fromJS(state: StoreData): AppStore {
 | 
				
			||||||
    const inst = new AppStore(state.spec.data, state.spec.url);
 | 
					    const inst = new AppStore(state.spec.data, state.spec.url, state.options);
 | 
				
			||||||
    inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
 | 
					    inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
 | 
				
			||||||
    inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
 | 
					    inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
 | 
				
			||||||
    return inst;
 | 
					    return inst;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { GroupModel, OperationModel } from './models';
 | 
				
			||||||
import { JsonPointer, isOperationName } from '../utils';
 | 
					import { JsonPointer, isOperationName } from '../utils';
 | 
				
			||||||
import { OpenAPIOperation, OpenAPIParameter, OpenAPISpec, OpenAPITag, Referenced } from '../types';
 | 
					import { OpenAPIOperation, OpenAPIParameter, OpenAPISpec, OpenAPITag, Referenced } from '../types';
 | 
				
			||||||
import { MarkdownRenderer } from './MarkdownRenderer';
 | 
					import { MarkdownRenderer } from './MarkdownRenderer';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from './RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TagInfo = OpenAPITag & {
 | 
					export type TagInfo = OpenAPITag & {
 | 
				
			||||||
  operations: ExtendedOpenAPIOperation[];
 | 
					  operations: ExtendedOpenAPIOperation[];
 | 
				
			||||||
| 
						 | 
					@ -29,16 +30,21 @@ export class MenuBuilder {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Builds page content structure based on tags
 | 
					   * Builds page content structure based on tags
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  static buildStructure(parser: OpenAPIParser): ContentItemModel[] {
 | 
					  static buildStructure(
 | 
				
			||||||
 | 
					    parser: OpenAPIParser,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
 | 
					  ): ContentItemModel[] {
 | 
				
			||||||
    const spec = parser.spec;
 | 
					    const spec = parser.spec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const items: ContentItemModel[] = [];
 | 
					    const items: ContentItemModel[] = [];
 | 
				
			||||||
    const tagsMap = MenuBuilder.getTagsWithOperations(spec);
 | 
					    const tagsMap = MenuBuilder.getTagsWithOperations(spec);
 | 
				
			||||||
    items.push(...MenuBuilder.addMarkdownItems(spec.info.description || ''));
 | 
					    items.push(...MenuBuilder.addMarkdownItems(spec.info.description || ''));
 | 
				
			||||||
    if (spec['x-tagGroups']) {
 | 
					    if (spec['x-tagGroups']) {
 | 
				
			||||||
      items.push(...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap));
 | 
					      items.push(
 | 
				
			||||||
 | 
					        ...MenuBuilder.getTagGroupsItems(parser, undefined, spec['x-tagGroups'], tagsMap, options),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      items.push(...MenuBuilder.getTagsItems(parser, tagsMap));
 | 
					      items.push(...MenuBuilder.getTagsItems(parser, tagsMap, undefined, undefined, options));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return items;
 | 
					    return items;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -62,12 +68,13 @@ export class MenuBuilder {
 | 
				
			||||||
    parent: GroupModel | undefined,
 | 
					    parent: GroupModel | undefined,
 | 
				
			||||||
    groups: TagGroup[],
 | 
					    groups: TagGroup[],
 | 
				
			||||||
    tags: TagsInfoMap,
 | 
					    tags: TagsInfoMap,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
  ): GroupModel[] {
 | 
					  ): GroupModel[] {
 | 
				
			||||||
    let res: GroupModel[] = [];
 | 
					    let res: GroupModel[] = [];
 | 
				
			||||||
    for (let group of groups) {
 | 
					    for (let group of groups) {
 | 
				
			||||||
      let item = new GroupModel('group', group, parent);
 | 
					      let item = new GroupModel('group', group, parent);
 | 
				
			||||||
      item.depth = GROUP_DEPTH;
 | 
					      item.depth = GROUP_DEPTH;
 | 
				
			||||||
      item.items = MenuBuilder.getTagsItems(parser, tags, item, group);
 | 
					      item.items = MenuBuilder.getTagsItems(parser, tags, item, group, options);
 | 
				
			||||||
      res.push(item);
 | 
					      res.push(item);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // TODO checkAllTagsUsedInGroups
 | 
					    // TODO checkAllTagsUsedInGroups
 | 
				
			||||||
| 
						 | 
					@ -83,8 +90,9 @@ export class MenuBuilder {
 | 
				
			||||||
  static getTagsItems(
 | 
					  static getTagsItems(
 | 
				
			||||||
    parser: OpenAPIParser,
 | 
					    parser: OpenAPIParser,
 | 
				
			||||||
    tagsMap: TagsInfoMap,
 | 
					    tagsMap: TagsInfoMap,
 | 
				
			||||||
    parent?: GroupModel,
 | 
					    parent: GroupModel | undefined,
 | 
				
			||||||
    group?: TagGroup,
 | 
					    group: TagGroup | undefined,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
  ): ContentItemModel[] {
 | 
					  ): ContentItemModel[] {
 | 
				
			||||||
    let tagNames;
 | 
					    let tagNames;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,11 +116,11 @@ export class MenuBuilder {
 | 
				
			||||||
      if (!tag) continue;
 | 
					      if (!tag) continue;
 | 
				
			||||||
      let item = new GroupModel('tag', tag, parent);
 | 
					      let item = new GroupModel('tag', tag, parent);
 | 
				
			||||||
      item.depth = GROUP_DEPTH + 1;
 | 
					      item.depth = GROUP_DEPTH + 1;
 | 
				
			||||||
      item.items = this.getOperationsItems(parser, item, tag, item.depth + 1);
 | 
					      item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // don't put empty tag into content, instead put its operations
 | 
					      // don't put empty tag into content, instead put its operations
 | 
				
			||||||
      if (tag.name === '') {
 | 
					      if (tag.name === '') {
 | 
				
			||||||
        let items = this.getOperationsItems(parser, undefined, tag, item.depth);
 | 
					        let items = this.getOperationsItems(parser, undefined, tag, item.depth, options);
 | 
				
			||||||
        res.push(...items);
 | 
					        res.push(...items);
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -133,6 +141,7 @@ export class MenuBuilder {
 | 
				
			||||||
    parent: GroupModel | undefined,
 | 
					    parent: GroupModel | undefined,
 | 
				
			||||||
    tag: TagInfo,
 | 
					    tag: TagInfo,
 | 
				
			||||||
    depth: number,
 | 
					    depth: number,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
  ): OperationModel[] {
 | 
					  ): OperationModel[] {
 | 
				
			||||||
    if (tag.operations.length === 0) {
 | 
					    if (tag.operations.length === 0) {
 | 
				
			||||||
      return [];
 | 
					      return [];
 | 
				
			||||||
| 
						 | 
					@ -140,7 +149,7 @@ export class MenuBuilder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let res: OperationModel[] = [];
 | 
					    let res: OperationModel[] = [];
 | 
				
			||||||
    for (let operationInfo of tag.operations) {
 | 
					    for (let operationInfo of tag.operations) {
 | 
				
			||||||
      let operation = new OperationModel(parser, operationInfo, parent);
 | 
					      let operation = new OperationModel(parser, operationInfo, parent, options);
 | 
				
			||||||
      operation.depth = depth;
 | 
					      operation.depth = depth;
 | 
				
			||||||
      res.push(operation);
 | 
					      res.push(operation);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,43 @@
 | 
				
			||||||
import { ThemeInterface } from '../theme';
 | 
					import { ThemeInterface } from '../theme';
 | 
				
			||||||
import { isNumeric } from '../utils/helpers';
 | 
					import { isNumeric } from '../utils/helpers';
 | 
				
			||||||
 | 
					import defaultTheme from '../theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RedocRawOptions {
 | 
					export interface RedocRawOptions {
 | 
				
			||||||
  theme?: ThemeInterface;
 | 
					  theme?: ThemeInterface;
 | 
				
			||||||
  scrollYOffset?: number | string | Function;
 | 
					  scrollYOffset?: number | string | Function;
 | 
				
			||||||
  hideHostname?: boolean | string;
 | 
					  hideHostname?: boolean | string;
 | 
				
			||||||
 | 
					  expandResponses?: string | 'all';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class RedocNormalizedOptions {
 | 
					export class RedocNormalizedOptions {
 | 
				
			||||||
  theme: ThemeInterface;
 | 
					  theme: ThemeInterface;
 | 
				
			||||||
  scrollYOffset: () => number;
 | 
					  scrollYOffset: () => number;
 | 
				
			||||||
  hideHostname: boolean;
 | 
					  hideHostname: boolean;
 | 
				
			||||||
 | 
					  expandResponses: { [code: string]: boolean } | 'all';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(raw: RedocRawOptions) {
 | 
					  constructor(raw: RedocRawOptions) {
 | 
				
			||||||
 | 
					    this.theme = { ...(raw.theme || {}), ...defaultTheme }; // todo: merge deep
 | 
				
			||||||
    this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
 | 
					    this.scrollYOffset = RedocNormalizedOptions.normalizeScrollYOffset(raw.scrollYOffset);
 | 
				
			||||||
    this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
 | 
					    this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
 | 
				
			||||||
 | 
					    this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static normalizeExpandResponses(value: RedocRawOptions['expandResponses']) {
 | 
				
			||||||
 | 
					    if (value === 'all') {
 | 
				
			||||||
 | 
					      return 'all';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (typeof value === 'string') {
 | 
				
			||||||
 | 
					      const res = {};
 | 
				
			||||||
 | 
					      value.split(',').forEach(code => {
 | 
				
			||||||
 | 
					        res[code.trim()] = true;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return res;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.warn(
 | 
				
			||||||
 | 
					        `expandResponses must be a string but received value "${value}" of type ${typeof value}`,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static normalizeHideHostname(value: RedocRawOptions['hideHostname']): boolean {
 | 
					  static normalizeHideHostname(value: RedocRawOptions['hideHostname']): boolean {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import { observable, computed } from 'mobx';
 | 
				
			||||||
import { MenuBuilder } from './MenuBuilder';
 | 
					import { MenuBuilder } from './MenuBuilder';
 | 
				
			||||||
import { OpenAPIParser } from './OpenAPIParser';
 | 
					import { OpenAPIParser } from './OpenAPIParser';
 | 
				
			||||||
import { ApiInfoModel } from './models/ApiInfo';
 | 
					import { ApiInfoModel } from './models/ApiInfo';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from './RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Store that containts all the specification related information in the form of tree
 | 
					 * Store that containts all the specification related information in the form of tree
 | 
				
			||||||
| 
						 | 
					@ -13,7 +14,11 @@ import { ApiInfoModel } from './models/ApiInfo';
 | 
				
			||||||
export class SpecStore {
 | 
					export class SpecStore {
 | 
				
			||||||
  @observable.ref parser: OpenAPIParser;
 | 
					  @observable.ref parser: OpenAPIParser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(spec: OpenAPISpec, specUrl?: string) {
 | 
					  constructor(
 | 
				
			||||||
 | 
					    spec: OpenAPISpec,
 | 
				
			||||||
 | 
					    specUrl: string | undefined,
 | 
				
			||||||
 | 
					    private options: RedocNormalizedOptions,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    this.parser = new OpenAPIParser(spec, specUrl);
 | 
					    this.parser = new OpenAPIParser(spec, specUrl);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +34,7 @@ export class SpecStore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @computed
 | 
					  @computed
 | 
				
			||||||
  get operationGroups() {
 | 
					  get operationGroups() {
 | 
				
			||||||
    return MenuBuilder.buildStructure(this.parser);
 | 
					    return MenuBuilder.buildStructure(this.parser, this.options);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @computed
 | 
					  @computed
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import { CodeSample } from './types';
 | 
				
			||||||
import { OpenAPIParser } from '../OpenAPIParser';
 | 
					import { OpenAPIParser } from '../OpenAPIParser';
 | 
				
			||||||
import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
 | 
					import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
 | 
				
			||||||
import { JsonPointer, getOperationSummary, isAbsolutePath, stripTrailingSlash } from '../../utils';
 | 
					import { JsonPointer, getOperationSummary, isAbsolutePath, stripTrailingSlash } from '../../utils';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isNumeric(n) {
 | 
					function isNumeric(n) {
 | 
				
			||||||
  return !isNaN(parseFloat(n)) && isFinite(n);
 | 
					  return !isNaN(parseFloat(n)) && isFinite(n);
 | 
				
			||||||
| 
						 | 
					@ -51,7 +52,12 @@ export class OperationModel implements IMenuItem {
 | 
				
			||||||
  servers: OpenAPIServer[];
 | 
					  servers: OpenAPIServer[];
 | 
				
			||||||
  codeSamples: CodeSample[];
 | 
					  codeSamples: CodeSample[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(parser: OpenAPIParser, operationSpec: ExtendedOpenAPIOperation, parent?: GroupModel) {
 | 
					  constructor(
 | 
				
			||||||
 | 
					    parser: OpenAPIParser,
 | 
				
			||||||
 | 
					    operationSpec: ExtendedOpenAPIOperation,
 | 
				
			||||||
 | 
					    parent: GroupModel | undefined,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
    this.id = operationSpec._$ref;
 | 
					    this.id = operationSpec._$ref;
 | 
				
			||||||
    this.name = getOperationSummary(operationSpec);
 | 
					    this.name = getOperationSummary(operationSpec);
 | 
				
			||||||
    this.description = operationSpec.description;
 | 
					    this.description = operationSpec.description;
 | 
				
			||||||
| 
						 | 
					@ -81,9 +87,15 @@ export class OperationModel implements IMenuItem {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return isNumeric(code) || code === 'default';
 | 
					        return isNumeric(code) || code === 'default';
 | 
				
			||||||
      }) // filter out other props (e.g. x-props)
 | 
					      }) // filter out other props (e.g. x-props)
 | 
				
			||||||
      .map(
 | 
					      .map(code => {
 | 
				
			||||||
        code => new ResponseModel(parser, code, hasSuccessResponses, operationSpec.responses[code]),
 | 
					        return new ResponseModel(
 | 
				
			||||||
 | 
					          parser,
 | 
				
			||||||
 | 
					          code,
 | 
				
			||||||
 | 
					          hasSuccessResponses,
 | 
				
			||||||
 | 
					          operationSpec.responses[code],
 | 
				
			||||||
 | 
					          options,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.servers = normalizeServers(
 | 
					    this.servers = normalizeServers(
 | 
				
			||||||
      parser.specUrl,
 | 
					      parser.specUrl,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@ import { FieldModel } from './Field';
 | 
				
			||||||
import { MediaContentModel } from './MediaContent';
 | 
					import { MediaContentModel } from './MediaContent';
 | 
				
			||||||
import { OpenAPIParser } from '../OpenAPIParser';
 | 
					import { OpenAPIParser } from '../OpenAPIParser';
 | 
				
			||||||
import { getStatusCodeType } from '../../utils';
 | 
					import { getStatusCodeType } from '../../utils';
 | 
				
			||||||
 | 
					import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ResponseModel {
 | 
					export class ResponseModel {
 | 
				
			||||||
  @observable public expanded: boolean = false;
 | 
					  @observable public expanded: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public content?: MediaContentModel;
 | 
					  public content?: MediaContentModel;
 | 
				
			||||||
  public code: string;
 | 
					  public code: string;
 | 
				
			||||||
| 
						 | 
					@ -16,7 +17,15 @@ export class ResponseModel {
 | 
				
			||||||
  public type: string;
 | 
					  public type: string;
 | 
				
			||||||
  public headers: FieldModel[] = [];
 | 
					  public headers: FieldModel[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(parser: OpenAPIParser, code: string, defaultAsError: boolean, infoOrRef: Referenced<OpenAPIResponse>) {
 | 
					  constructor(
 | 
				
			||||||
 | 
					    parser: OpenAPIParser,
 | 
				
			||||||
 | 
					    code: string,
 | 
				
			||||||
 | 
					    defaultAsError: boolean,
 | 
				
			||||||
 | 
					    infoOrRef: Referenced<OpenAPIResponse>,
 | 
				
			||||||
 | 
					    options: RedocNormalizedOptions,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.expanded = options.expandResponses === 'all' || options.expandResponses[code];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const info = parser.deref(infoOrRef);
 | 
					    const info = parser.deref(infoOrRef);
 | 
				
			||||||
    parser.exitRef(infoOrRef);
 | 
					    parser.exitRef(infoOrRef);
 | 
				
			||||||
    this.code = code;
 | 
					    this.code = code;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user