mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 06:04:56 +03:00
Merge branch 'master' into external-docs
This commit is contained in:
commit
964d5237b6
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -1,3 +1,36 @@
|
||||||
|
<a name="2.0.0-alpha.34"></a>
|
||||||
|
# [2.0.0-alpha.34](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.33...v2.0.0-alpha.34) (2018-08-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add some spacing between operation description and parameters ([597688e](https://github.com/Rebilly/ReDoc/commit/597688e))
|
||||||
|
* description is not rendered if doesn't containt markdown headings ([90ed717](https://github.com/Rebilly/ReDoc/commit/90ed717)), closes [#591](https://github.com/Rebilly/ReDoc/issues/591)
|
||||||
|
* download button downloads index.html instead of spec with CLI ([334f904](https://github.com/Rebilly/ReDoc/commit/334f904)), closes [#594](https://github.com/Rebilly/ReDoc/issues/594)
|
||||||
|
* fix Authentication section is not rendered ([2ecc8bc](https://github.com/Rebilly/ReDoc/commit/2ecc8bc)), closes [#590](https://github.com/Rebilly/ReDoc/issues/590)
|
||||||
|
* fix linebreaks in multiparagraph field descriptions ([8fb9cd6](https://github.com/Rebilly/ReDoc/commit/8fb9cd6))
|
||||||
|
* preserve md heading level in description ([23559fb](https://github.com/Rebilly/ReDoc/commit/23559fb))
|
||||||
|
* render additionalProperties set to true ([#597](https://github.com/Rebilly/ReDoc/issues/597)) ([f70ac08](https://github.com/Rebilly/ReDoc/commit/f70ac08)), closes [#596](https://github.com/Rebilly/ReDoc/issues/596)
|
||||||
|
* schemes without type: object are not expandable ([97e1620](https://github.com/Rebilly/ReDoc/commit/97e1620)), closes [#599](https://github.com/Rebilly/ReDoc/issues/599)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add x-logo alt text support ([#584](https://github.com/Rebilly/ReDoc/issues/584)) ([568ce74](https://github.com/Rebilly/ReDoc/commit/568ce74)), closes [#546](https://github.com/Rebilly/ReDoc/issues/546)
|
||||||
|
* support label for x-code-samples ([00bd966](https://github.com/Rebilly/ReDoc/commit/00bd966)), closes [#586](https://github.com/Rebilly/ReDoc/issues/586)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0-alpha.33"></a>
|
||||||
|
# [2.0.0-alpha.33](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.32...v2.0.0-alpha.33) (2018-07-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* long endpoint url overflow ([d99e918](https://github.com/Rebilly/ReDoc/commit/d99e918))
|
||||||
|
* allow word-break in code strings in md ([15dfe44](https://github.com/Rebilly/ReDoc/commit/15dfe44))
|
||||||
|
* show examples for response headers ([ba22b1e](https://github.com/Rebilly/ReDoc/commit/ba22b1e))
|
||||||
|
|
||||||
<a name="2.0.0-alpha.32"></a>
|
<a name="2.0.0-alpha.32"></a>
|
||||||
# [2.0.0-alpha.32](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.31...v2.0.0-alpha.32) (2018-07-26)
|
# [2.0.0-alpha.32](https://github.com/Rebilly/ReDoc/compare/v2.0.0-alpha.31...v2.0.0-alpha.32) (2018-07-26)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redoc",
|
"name": "redoc",
|
||||||
"version": "2.0.0-alpha.33",
|
"version": "2.0.0-alpha.34",
|
||||||
"description": "ReDoc",
|
"description": "ReDoc",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -82,7 +82,7 @@ export class ApiInfo extends React.Component<ApiInfoProps> {
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
download={downloadFilename}
|
download={downloadFilename}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={downloadLink || '#'}
|
href={downloadLink}
|
||||||
onClick={this.handleDownloadClick}
|
onClick={this.handleDownloadClick}
|
||||||
>
|
>
|
||||||
Download
|
Download
|
||||||
|
|
|
@ -20,6 +20,7 @@ export const DownloadButton = styled.a`
|
||||||
padding: 4px 8px 4px;
|
padding: 4px 8px 4px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
${extensionsHook('DownloadButton')};
|
${extensionsHook('DownloadButton')};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -5,10 +5,10 @@ import { SECTION_ATTR } from '../../services/MenuStore';
|
||||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
|
|
||||||
import { H1, MiddlePanel, Row, ShareLink } from '../../common-elements';
|
import { H1, H2, MiddlePanel, Row, ShareLink } from '../../common-elements';
|
||||||
import { MDXComponentMeta } from '../../services/MarkdownRenderer';
|
import { MDXComponentMeta } from '../../services/MarkdownRenderer';
|
||||||
import { ContentItemModel } from '../../services/MenuBuilder';
|
import { ContentItemModel } from '../../services/MenuBuilder';
|
||||||
import { OperationModel } from '../../services/models';
|
import { GroupModel, OperationModel } from '../../services/models';
|
||||||
import { Operation } from '../Operation/Operation';
|
import { Operation } from '../Operation/Operation';
|
||||||
import { SecurityDefs } from '../SecuritySchemes/SecuritySchemes';
|
import { SecurityDefs } from '../SecuritySchemes/SecuritySchemes';
|
||||||
import { StoreConsumer } from '../StoreBuilder';
|
import { StoreConsumer } from '../StoreBuilder';
|
||||||
|
@ -80,23 +80,24 @@ export class ContentItem extends React.Component<ContentItemProps> {
|
||||||
@observer
|
@observer
|
||||||
export class SectionItem extends React.Component<ContentItemProps> {
|
export class SectionItem extends React.Component<ContentItemProps> {
|
||||||
render() {
|
render() {
|
||||||
const { name, description, externalDocs } = this.props.item;
|
const { name, description, externalDocs, level } = this.props.item as GroupModel;
|
||||||
const components = this.props.allowedMdComponents;
|
const components = this.props.allowedMdComponents;
|
||||||
|
const Header = level === 2 ? H2 : H1;
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
<MiddlePanel>
|
<MiddlePanel>
|
||||||
<H1>
|
<Header>
|
||||||
<ShareLink href={'#' + this.props.item.id} />
|
<ShareLink href={'#' + this.props.item.id} />
|
||||||
{name}
|
{name}
|
||||||
</H1>
|
</Header>
|
||||||
{components ? (
|
{components ? (
|
||||||
<Markdown source={description || ''} />
|
|
||||||
) : (
|
|
||||||
<StoreConsumer>
|
<StoreConsumer>
|
||||||
{store => (
|
{store => (
|
||||||
<Markdown source={description || ''} allowedComponents={components} store={store} />
|
<Markdown source={description || ''} allowedComponents={components} store={store} />
|
||||||
)}
|
)}
|
||||||
</StoreConsumer>
|
</StoreConsumer>
|
||||||
|
) : (
|
||||||
|
<Markdown source={description || ''} />
|
||||||
)}
|
)}
|
||||||
{externalDocs && (
|
{externalDocs && (
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -26,16 +26,21 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
line-height: ${props => props.theme.typography.lineHeight};
|
line-height: ${props => props.theme.typography.lineHeight};
|
||||||
|
|
||||||
p {
|
p {
|
||||||
&:last-of-type {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${({ dense }) =>
|
${({ dense }) =>
|
||||||
dense &&
|
dense &&
|
||||||
` p {
|
`
|
||||||
margin: 0;
|
p:first-child {
|
||||||
}`}
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
|
||||||
${({ inline }) =>
|
${({ inline }) =>
|
||||||
inline &&
|
inline &&
|
||||||
|
@ -108,9 +113,9 @@ export const StyledMarkdownBlock = withProps<{ dense?: boolean; inline?: boolean
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
> li {
|
// > li {
|
||||||
margin: 1em 0;
|
// margin: 0.5em 0;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { ResponsesList } from '../Responses/ResponsesList';
|
||||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||||
|
|
||||||
import { OperationModel as OperationType } from '../../services/models';
|
import { OperationModel as OperationType } from '../../services/models';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
const OperationRow = Row.extend`
|
const OperationRow = Row.extend`
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
|
@ -35,6 +36,10 @@ const OperationRow = Row.extend`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Description = styled(Markdown)`
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing.unit * 8};
|
||||||
|
`;
|
||||||
|
|
||||||
export interface OperationProps {
|
export interface OperationProps {
|
||||||
operation: OperationType;
|
operation: OperationType;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +60,7 @@ export class Operation extends React.Component<OperationProps> {
|
||||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||||
</H2>
|
</H2>
|
||||||
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
||||||
{description !== undefined && <Markdown source={description} />}
|
{description !== undefined && <Description source={description} />}
|
||||||
{externalDocs && (
|
{externalDocs && (
|
||||||
<p>
|
<p>
|
||||||
<ExternalDocumentation externalDocs={externalDocs} />
|
<ExternalDocumentation externalDocs={externalDocs} />
|
||||||
|
|
|
@ -25,6 +25,7 @@ export interface MDXComponentMeta {
|
||||||
export interface MarkdownHeading {
|
export interface MarkdownHeading {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
level: number;
|
||||||
items?: MarkdownHeading[];
|
items?: MarkdownHeading[];
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
@ -50,12 +51,14 @@ export class MarkdownRenderer {
|
||||||
|
|
||||||
saveHeading(
|
saveHeading(
|
||||||
name: string,
|
name: string,
|
||||||
|
level: number,
|
||||||
container: MarkdownHeading[] = this.headings,
|
container: MarkdownHeading[] = this.headings,
|
||||||
parentId?: string,
|
parentId?: string,
|
||||||
): MarkdownHeading {
|
): MarkdownHeading {
|
||||||
const item = {
|
const item = {
|
||||||
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
id: parentId ? `${parentId}/${safeSlugify(name)}` : `section/${safeSlugify(name)}`,
|
||||||
name,
|
name,
|
||||||
|
level,
|
||||||
items: [],
|
items: [],
|
||||||
};
|
};
|
||||||
container.push(item);
|
container.push(item);
|
||||||
|
@ -105,10 +108,11 @@ export class MarkdownRenderer {
|
||||||
|
|
||||||
headingRule = (text: string, level: number, raw: string) => {
|
headingRule = (text: string, level: number, raw: string) => {
|
||||||
if (level === 1) {
|
if (level === 1) {
|
||||||
this.currentTopHeading = this.saveHeading(text);
|
this.currentTopHeading = this.saveHeading(text, level);
|
||||||
} else if (level === 2) {
|
} else if (level === 2) {
|
||||||
this.saveHeading(
|
this.saveHeading(
|
||||||
text,
|
text,
|
||||||
|
level,
|
||||||
this.currentTopHeading && this.currentTopHeading.items,
|
this.currentTopHeading && this.currentTopHeading.items,
|
||||||
this.currentTopHeading && this.currentTopHeading.id,
|
this.currentTopHeading && this.currentTopHeading.id,
|
||||||
);
|
);
|
||||||
|
|
|
@ -76,11 +76,11 @@ export class OpenAPIParser {
|
||||||
const description = spec.info.description || '';
|
const description = spec.info.description || '';
|
||||||
const legacySecurityRegexp = new RegExp(
|
const legacySecurityRegexp = new RegExp(
|
||||||
COMPONENT_REGEXP.replace('{component}', '<security-definitions>'),
|
COMPONENT_REGEXP.replace('{component}', '<security-definitions>'),
|
||||||
'gmi',
|
'mi',
|
||||||
);
|
);
|
||||||
const securityRegexp = new RegExp(
|
const securityRegexp = new RegExp(
|
||||||
MDX_COMPONENT_REGEXP.replace('{component}', 'security-definitions'),
|
MDX_COMPONENT_REGEXP.replace('{component}', 'security-definitions'),
|
||||||
'gmi',
|
'mi',
|
||||||
);
|
);
|
||||||
if (!legacySecurityRegexp.test(description) && !securityRegexp.test(description)) {
|
if (!legacySecurityRegexp.test(description) && !securityRegexp.test(description)) {
|
||||||
const comment = buildComponentComment('security-definitions');
|
const comment = buildComponentComment('security-definitions');
|
||||||
|
|
38
src/services/__tests__/models/ApiInfo.test.ts
Normal file
38
src/services/__tests__/models/ApiInfo.test.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { ApiInfoModel } from '../../models/ApiInfo';
|
||||||
|
import { OpenAPIParser } from '../../OpenAPIParser';
|
||||||
|
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
|
||||||
|
|
||||||
|
const opts = new RedocNormalizedOptions({});
|
||||||
|
describe('Models', () => {
|
||||||
|
describe('ResponseModel', () => {
|
||||||
|
let parser: OpenAPIParser;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
parser = new OpenAPIParser({ openapi: '3.0.0' } as any, undefined, opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should correctly populate description field without md headings', () => {
|
||||||
|
parser.spec = {
|
||||||
|
openapi: '3.0.0',
|
||||||
|
info: {
|
||||||
|
description: 'Test description',
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const info = new ApiInfoModel(parser);
|
||||||
|
expect(info.description).toEqual('Test description');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should correctly populate description up to the first md heading', () => {
|
||||||
|
parser.spec = {
|
||||||
|
openapi: '3.0.0',
|
||||||
|
info: {
|
||||||
|
description: 'Test description\nsome text\n## Heading\n test',
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const info = new ApiInfoModel(parser);
|
||||||
|
expect(info.description).toEqual('Test description\nsome text\n');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -23,12 +23,12 @@ describe('Models', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default should be sucessful by default', () => {
|
test('default should be sucessful by default', () => {
|
||||||
let resp = new ResponseModel(parser, 'default', false, {}, opts);
|
const resp = new ResponseModel(parser, 'default', false, {}, opts);
|
||||||
expect(resp.type).toEqual('success');
|
expect(resp.type).toEqual('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default should be error if defaultAsError is true', () => {
|
test('default should be error if defaultAsError is true', () => {
|
||||||
let resp = new ResponseModel(parser, 'default', true, {}, opts);
|
const resp = new ResponseModel(parser, 'default', true, {}, opts);
|
||||||
expect(resp.type).toEqual('error');
|
expect(resp.type).toEqual('error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,10 @@ export class ApiInfoModel implements OpenAPIInfo {
|
||||||
constructor(private parser: OpenAPIParser) {
|
constructor(private parser: OpenAPIParser) {
|
||||||
Object.assign(this, parser.spec.info);
|
Object.assign(this, parser.spec.info);
|
||||||
this.description = parser.spec.info.description || '';
|
this.description = parser.spec.info.description || '';
|
||||||
this.description = this.description.substring(0, this.description.search(/^##?\s+/m));
|
const firstHeadingLinePos = this.description.search(/^##?\s+/m);
|
||||||
|
if (firstHeadingLinePos > -1) {
|
||||||
|
this.description = this.description.substring(0, firstHeadingLinePos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get downloadLink(): string | undefined {
|
get downloadLink(): string | undefined {
|
||||||
|
|
|
@ -25,6 +25,7 @@ export class GroupModel implements IMenuItem {
|
||||||
@observable expanded: boolean = false;
|
@observable expanded: boolean = false;
|
||||||
|
|
||||||
depth: number;
|
depth: number;
|
||||||
|
level: number;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -36,6 +37,7 @@ export class GroupModel implements IMenuItem {
|
||||||
this.id = (tagOrGroup as MarkdownHeading).id || type + '/' + safeSlugify(tagOrGroup.name);
|
this.id = (tagOrGroup as MarkdownHeading).id || type + '/' + safeSlugify(tagOrGroup.name);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
this.name = tagOrGroup['x-displayName'] || tagOrGroup.name;
|
||||||
|
this.level = (tagOrGroup as MarkdownHeading).level || 1;
|
||||||
this.description = tagOrGroup.description || '';
|
this.description = tagOrGroup.description || '';
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
|
||||||
|
|
|
@ -106,7 +106,7 @@ export class SchemaModel {
|
||||||
this.constraints = humanizeConstraints(schema);
|
this.constraints = humanizeConstraints(schema);
|
||||||
this.displayType = this.type;
|
this.displayType = this.type;
|
||||||
this.displayFormat = this.format;
|
this.displayFormat = this.format;
|
||||||
this.isPrimitive = isPrimitiveType(schema);
|
this.isPrimitive = isPrimitiveType(schema, this.type);
|
||||||
this.default = schema.default;
|
this.default = schema.default;
|
||||||
this.readOnly = !!schema.readOnly;
|
this.readOnly = !!schema.readOnly;
|
||||||
this.writeOnly = !!schema.writeOnly;
|
this.writeOnly = !!schema.writeOnly;
|
||||||
|
@ -246,14 +246,14 @@ function buildFields(
|
||||||
sortByRequired(fields, schema.required);
|
sortByRequired(fields, schema.required);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof additionalProps === 'object') {
|
if (typeof additionalProps === 'object' || additionalProps === true) {
|
||||||
fields.push(
|
fields.push(
|
||||||
new FieldModel(
|
new FieldModel(
|
||||||
parser,
|
parser,
|
||||||
{
|
{
|
||||||
name: 'property name *',
|
name: 'property name *',
|
||||||
required: false,
|
required: false,
|
||||||
schema: additionalProps,
|
schema: additionalProps === true ? {} : additionalProps,
|
||||||
kind: 'additionalProperties',
|
kind: 'additionalProperties',
|
||||||
},
|
},
|
||||||
$ref + '/additionalProperties',
|
$ref + '/additionalProperties',
|
||||||
|
|
|
@ -187,6 +187,17 @@ describe('Utils', () => {
|
||||||
};
|
};
|
||||||
expect(isPrimitiveType(schema)).toEqual(false);
|
expect(isPrimitiveType(schema)).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work with externally provided type', () => {
|
||||||
|
const schema = {
|
||||||
|
properties: {
|
||||||
|
a: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(isPrimitiveType(schema, 'object')).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('openapi mergeParams', () => {
|
describe('openapi mergeParams', () => {
|
||||||
|
|
|
@ -105,18 +105,18 @@ export function detectType(schema: OpenAPISchema): string {
|
||||||
return 'any';
|
return 'any';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPrimitiveType(schema: OpenAPISchema) {
|
export function isPrimitiveType(schema: OpenAPISchema, type: string | undefined = schema.type) {
|
||||||
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
if (schema.oneOf !== undefined || schema.anyOf !== undefined) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.type === 'object') {
|
if (type === 'object') {
|
||||||
return schema.properties !== undefined
|
return schema.properties !== undefined
|
||||||
? Object.keys(schema.properties).length === 0
|
? Object.keys(schema.properties).length === 0
|
||||||
: schema.additionalProperties === undefined;
|
: schema.additionalProperties === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.type === 'array') {
|
if (type === 'array') {
|
||||||
if (schema.items === undefined) {
|
if (schema.items === undefined) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user