Implement noAutoAuth option

This commit is contained in:
Roman Hotsiy 2017-11-21 16:33:22 +02:00
parent a28e24ec92
commit f607b98866
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
9 changed files with 84 additions and 9 deletions

View File

@ -8,30 +8,31 @@ import { Schema } from '../Schema';
import { ObjectSchema } from '../ObjectSchema'; import { ObjectSchema } from '../ObjectSchema';
import * as simpleDiscriminatorFixture from './fixtures/simple-discriminator.json'; import * as simpleDiscriminatorFixture from './fixtures/simple-discriminator.json';
const options = new RedocNormalizedOptions({});
describe('Components', () => { describe('Components', () => {
describe('SchemaView', () => { describe('SchemaView', () => {
describe('discriminator', () => { describe('discriminator', () => {
it('should correctly render SchemaView', () => { it('should correctly render SchemaView', () => {
const parser = new OpenAPIParser(simpleDiscriminatorFixture); const parser = new OpenAPIParser(simpleDiscriminatorFixture, undefined, options);
const schema = new SchemaModel( const schema = new SchemaModel(
parser, parser,
{ $ref: '#/components/schemas/Pet' }, { $ref: '#/components/schemas/Pet' },
'#/components/schemas/Pet', '#/components/schemas/Pet',
new RedocNormalizedOptions({}), options,
); );
const schemaView = shallow(<Schema schema={schema} />); const schemaView = shallow(<Schema schema={schema} />);
expect(toJson(schemaView)).toMatchSnapshot(); expect(toJson(schemaView)).toMatchSnapshot();
}); });
it('should correctly render discriminator dropdown', () => { it('should correctly render discriminator dropdown', () => {
const parser = new OpenAPIParser(simpleDiscriminatorFixture); const parser = new OpenAPIParser(simpleDiscriminatorFixture, undefined, options);
const schema = new SchemaModel( const schema = new SchemaModel(
parser, parser,
{ $ref: '#/components/schemas/Pet' }, { $ref: '#/components/schemas/Pet' },
'#/components/schemas/Pet', '#/components/schemas/Pet',
new RedocNormalizedOptions({}), options,
); );
const schemaView = shallow( const schemaView = shallow(
<ObjectSchema <ObjectSchema

View File

@ -57,6 +57,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -157,6 +158,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -240,6 +242,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -377,6 +380,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -477,6 +481,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -560,6 +565,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -667,6 +673,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -800,6 +807,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -900,6 +908,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -983,6 +992,7 @@ exports[`Components SchemaView discriminator should correctly render SchemaView
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -1118,6 +1128,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {
@ -1225,6 +1236,7 @@ exports[`Components SchemaView discriminator should correctly render discriminat
"options": RedocNormalizedOptions { "options": RedocNormalizedOptions {
"expandResponses": Object {}, "expandResponses": Object {},
"hideHostname": false, "hideHostname": false,
"noAutoAuth": false,
"requiredPropsFirst": false, "requiredPropsFirst": false,
"scrollYOffset": [Function], "scrollYOffset": [Function],
"theme": Object { "theme": Object {

View File

@ -13,7 +13,10 @@ const md = new Remarkable('default', {
}, },
}); });
const COMPONENT_REGEXP = '^\\s*<!-- ReDoc-Inject:\\s+?{component}\\s+?-->\\s*$'; export const COMPONENT_REGEXP = '^\\s*<!-- ReDoc-Inject:\\s+?{component}\\s+?-->\\s*$';
export function buildComponentComment(name: string) {
return `<!-- ReDoc-Inject: <${name}> -->`;
}
type MarkdownHeading = { type MarkdownHeading = {
name: string; name: string;

View File

@ -5,6 +5,9 @@ import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types';
import { JsonPointer } from '../utils/JsonPointer'; import { JsonPointer } from '../utils/JsonPointer';
import { isNamedDefinition } from '../utils/openapi'; import { isNamedDefinition } from '../utils/openapi';
import { COMPONENT_REGEXP, buildComponentComment } from './MarkdownRenderer';
import { RedocNormalizedOptions } from './RedocNormalizedOptions';
import { appendToMdHeading } from '../utils/index';
export type MergedOpenAPISchema = OpenAPISchema & { namedParents?: string[] }; export type MergedOpenAPISchema = OpenAPISchema & { namedParents?: string[] };
@ -39,8 +42,13 @@ export class OpenAPIParser {
@observable specUrl: string; @observable specUrl: string;
@observable.ref spec: OpenAPISpec; @observable.ref spec: OpenAPISpec;
constructor(spec: OpenAPISpec, specUrl?: string) { constructor(
spec: OpenAPISpec,
specUrl: string | undefined,
private options: RedocNormalizedOptions,
) {
this.validate(spec); this.validate(spec);
this.preprocess(spec);
this.spec = spec; this.spec = spec;
@ -59,6 +67,22 @@ export class OpenAPIParser {
} }
} }
preprocess(spec: OpenAPISpec) {
if (!this.options.noAutoAuth && spec.info) {
// Automatically inject Authentication section with SecurityDefinitions component
const description = spec.info.description || '';
const securityRegexp = new RegExp(
COMPONENT_REGEXP.replace('{component}', '<security-definitions>'),
'gmi',
);
debugger;
if (!securityRegexp.test(description)) {
const comment = buildComponentComment('security-definitions');
spec.info.description = appendToMdHeading(description, 'Authentication', comment);
}
}
}
/** /**
* get spec part by JsonPointer ($ref) * get spec part by JsonPointer ($ref)
*/ */

View File

@ -8,6 +8,7 @@ export interface RedocRawOptions {
hideHostname?: boolean | string; hideHostname?: boolean | string;
expandResponses?: string | 'all'; expandResponses?: string | 'all';
requiredPropsFirst?: boolean | string; requiredPropsFirst?: boolean | string;
noAutoAuth?: boolean | string;
} }
function argValueToBoolean(val?: string | boolean): boolean { function argValueToBoolean(val?: string | boolean): boolean {
@ -22,6 +23,7 @@ export class RedocNormalizedOptions {
hideHostname: boolean; hideHostname: boolean;
expandResponses: { [code: string]: boolean } | 'all'; expandResponses: { [code: string]: boolean } | 'all';
requiredPropsFirst: boolean; requiredPropsFirst: boolean;
noAutoAuth: boolean;
constructor(raw: RedocRawOptions) { constructor(raw: RedocRawOptions) {
this.theme = { ...(raw.theme || {}), ...defaultTheme }; // todo: merge deep this.theme = { ...(raw.theme || {}), ...defaultTheme }; // todo: merge deep
@ -29,6 +31,7 @@ export class RedocNormalizedOptions {
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname); this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses); this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst); this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
} }
static normalizeExpandResponses(value: RedocRawOptions['expandResponses']) { static normalizeExpandResponses(value: RedocRawOptions['expandResponses']) {

View File

@ -19,7 +19,7 @@ export class SpecStore {
specUrl: string | undefined, specUrl: string | undefined,
private options: RedocNormalizedOptions, private options: RedocNormalizedOptions,
) { ) {
this.parser = new OpenAPIParser(spec, specUrl); this.parser = new OpenAPIParser(spec, specUrl, options);
} }
@computed @computed

View File

@ -8,7 +8,7 @@ describe('Models', () => {
let parser; let parser;
beforeEach(() => { beforeEach(() => {
parser = new OpenAPIParser({ openapi: '3.0.0' } as any); parser = new OpenAPIParser({ openapi: '3.0.0' } as any, undefined, opts);
}); });
test('should calculate response type based on code', () => { test('should calculate response type based on code', () => {

View File

@ -1,4 +1,4 @@
import { mapWithLast } from '../helpers'; import { mapWithLast, appendToMdHeading } from '../helpers';
describe('Utils', () => { describe('Utils', () => {
describe('helpers', () => { describe('helpers', () => {
@ -19,5 +19,24 @@ describe('Utils', () => {
const expected = []; const expected = [];
expect(actual).toEqual(expected); expect(actual).toEqual(expected);
}); });
test('appendToMdHeading heading exists not last', () => {
const val = appendToMdHeading(
'# Authentication\n Hello\n# Next heading',
'Authentication',
'<test>',
);
expect(val).toEqual('# Authentication\n Hello\n\n<test>\n\n# Next heading');
});
test('appendToMdHeading heading exists last', () => {
const val = appendToMdHeading('# Authentication\n Hello', 'Authentication', '<test>');
expect(val).toEqual('# Authentication\n Hello\n\n<test>\n');
});
test('appendToMdHeading empty string', () => {
const val = appendToMdHeading('', 'Authentication', '<test>');
expect(val).toEqual('# Authentication\n\n<test>');
});
}); });
}); });

View File

@ -67,3 +67,16 @@ export function isAbsolutePath(path: string): boolean {
export function isNumeric(n: any): n is Number { export function isNumeric(n: any): n is Number {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
} }
export function appendToMdHeading(md: string, heading: string, content: string) {
// if heading is already in md and append to the end of it
const testRegex = new RegExp(`(^|\\n)#\\s?${heading}\\s*\\n`, 'i');
const replaceRegex = new RegExp(`((\\n|^)#\\s*${heading}\\s*(\\n|$)(?:.|\\n)*?)(\\n#|$)`, 'i');
if (testRegex.test(md)) {
return md.replace(replaceRegex, `$1\n\n${content}\n$4`);
} else {
// else append heading itself
const br = md === '' || md.endsWith('\n\n') ? '' : md.endsWith('\n') ? '\n' : '\n\n';
return `${md}${br}# ${heading}\n\n${content}`;
}
}