diff --git a/e2e/integration/menu.e2e.ts b/e2e/integration/menu.e2e.ts index e1b053d125..fc703b6c12 100644 --- a/e2e/integration/menu.e2e.ts +++ b/e2e/integration/menu.e2e.ts @@ -6,7 +6,7 @@ describe('Menu', () => { it('should have valid items count', () => { cy.get('.menu-content') .find('li') - .should('have.length', 34); + .should('have.length', 33); }); it('should sync active menu items while scroll', () => { diff --git a/package-lock.json b/package-lock.json index 35abf30297..7183d16929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1222,9 +1222,9 @@ } }, "@redocly/openapi-core": { - "version": "1.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.44.tgz", - "integrity": "sha512-9HNnh1MzvMsLK1liuidFBqWiAsZ2Yg3RY58fcEsy0QruSMdDbn7SoeI1qnXe6O+BkBS+vAP4oVzZDMHCMKGsOQ==", + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.0.0-beta.46.tgz", + "integrity": "sha512-w4VG2KNLFDuZgN7fBmbzbI0GJDiPnJ0SYszj4uuSkMW35SVTvDWyTaeWjW8ggQJ3gluDnTgUvm9tjLdR2tLqUg==", "requires": { "@redocly/ajv": "^6.12.3", "@types/node": "^14.11.8", @@ -1238,9 +1238,9 @@ }, "dependencies": { "@types/node": { - "version": "14.14.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz", - "integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==" + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.0.tgz", + "integrity": "sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA==" } } }, diff --git a/package.json b/package.json index 7f80c432f0..d8fe29a448 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "styled-components": "^4.1.1 || ^5.1.1" }, "dependencies": { - "@redocly/openapi-core": "^1.0.0-beta.44", + "@redocly/openapi-core": "^1.0.0-beta.45", "@redocly/react-dropdown-aria": "^2.0.11", "@types/node": "^13.11.1", "classnames": "^2.2.6", diff --git a/src/common-elements/fields.ts b/src/common-elements/fields.ts index de7c651329..c8d0df1b0d 100644 --- a/src/common-elements/fields.ts +++ b/src/common-elements/fields.ts @@ -61,11 +61,6 @@ export const RecursiveLabel = styled(FieldLabel)` font-size: 13px; `; -export const NullableLabel = styled(FieldLabel)` - color: #0e7c86; - font-size: 13px; -`; - export const PatternLabel = styled(FieldLabel)` color: #0e7c86; &::before, diff --git a/src/components/Fields/EnumValues.tsx b/src/components/Fields/EnumValues.tsx index 978426f665..d10f9f692a 100644 --- a/src/components/Fields/EnumValues.tsx +++ b/src/components/Fields/EnumValues.tsx @@ -8,7 +8,7 @@ import { RedocRawOptions } from '../../services/RedocNormalizedOptions'; export interface EnumValuesProps { values: string[]; - type: string; + type: string | string[]; } export interface EnumValuesState { diff --git a/src/components/Fields/FieldDetails.tsx b/src/components/Fields/FieldDetails.tsx index 980eff4fb3..ac362a4059 100644 --- a/src/components/Fields/FieldDetails.tsx +++ b/src/components/Fields/FieldDetails.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { - NullableLabel, PatternLabel, RecursiveLabel, TypeFormat, @@ -79,7 +78,6 @@ export class FieldDetails extends React.PureComponent ({schema.title}) } - {schema.nullable && {l('nullable')} } {schema.pattern && !hideSchemaPattern && ( <> diff --git a/src/components/Redoc/Redoc.tsx b/src/components/Redoc/Redoc.tsx index 2b43e77d0a..a3d0eef7cd 100644 --- a/src/components/Redoc/Redoc.tsx +++ b/src/components/Redoc/Redoc.tsx @@ -39,7 +39,7 @@ export class Redoc extends React.Component { const store = this.props.store; return ( - + diff --git a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap index 745c4cad4c..f660e397d3 100644 --- a/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DiscriminatorDropdown.test.tsx.snap @@ -29,7 +29,6 @@ exports[`Components SchemaView discriminator should correctly render discriminat "format": undefined, "isCircular": undefined, "isPrimitive": true, - "nullable": false, "options": "<<>>", "pattern": undefined, "pointer": "#/components/schemas/Dog/properties/packSize", @@ -79,7 +78,6 @@ exports[`Components SchemaView discriminator should correctly render discriminat "format": undefined, "isCircular": undefined, "isPrimitive": true, - "nullable": false, "options": "<<>>", "pattern": undefined, "pointer": "#/components/schemas/Dog/properties/type", diff --git a/src/services/Labels.ts b/src/services/Labels.ts index c3568eb1ef..f5fa1fd14a 100644 --- a/src/services/Labels.ts +++ b/src/services/Labels.ts @@ -6,7 +6,6 @@ export interface LabelsConfig { deprecated: string; example: string; examples: string; - nullable: string; recursive: string; arrayOf: string; webhook: string; @@ -22,7 +21,6 @@ const labels: LabelsConfig = { deprecated: 'Deprecated', example: 'Example', examples: 'Examples', - nullable: 'Nullable', recursive: 'Recursive', arrayOf: 'Array of ', webhook: 'Event', diff --git a/src/services/MenuBuilder.ts b/src/services/MenuBuilder.ts index 339ba6cf18..69e2cc619f 100644 --- a/src/services/MenuBuilder.ts +++ b/src/services/MenuBuilder.ts @@ -222,8 +222,8 @@ export class MenuBuilder { } getTags(spec.paths); - if (spec['x-webhooks']) { - getTags(spec['x-webhooks'], true); + if (spec.webhooks) { + getTags(spec.webhooks, true); } function getTags(paths: OpenAPIPaths, isWebhook?: boolean) { diff --git a/src/services/models/Schema.ts b/src/services/models/Schema.ts index e6fbe4345d..fb0c516442 100644 --- a/src/services/models/Schema.ts +++ b/src/services/models/Schema.ts @@ -25,7 +25,7 @@ import { l } from '../Labels'; export class SchemaModel { pointer: string; - type: string; + type: string | string[]; displayType: string; typePrefix: string = ''; title: string; @@ -77,11 +77,6 @@ export class SchemaModel { this.pointer = schemaOrRef.$ref || pointer || ''; this.rawSchema = parser.deref(schemaOrRef); - if (Array.isArray(this.rawSchema.type)) { - this.rawSchema.oneOf = this.rawSchema.type.map( type => ({type})); - delete this.rawSchema.type; - } - this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild); this.init(parser, isChild); @@ -110,9 +105,8 @@ export class SchemaModel { this.title = schema.title || (isNamedDefinition(this.pointer) && JsonPointer.baseName(this.pointer)) || ''; this.description = schema.description || ''; - this.type = schema.type || detectType(schema); + this.type = (Array.isArray(schema.type) && schema.type) || (schema.type || detectType(schema)); this.format = schema.format; - this.nullable = !!schema.nullable; this.enum = schema.enum || []; this.example = schema.example; this.deprecated = !!schema.deprecated; @@ -120,13 +114,19 @@ export class SchemaModel { this.externalDocs = schema.externalDocs; this.constraints = humanizeConstraints(schema); - this.displayType = Array.isArray(this.type) ? this.type.join(' or ') : this.type; this.displayFormat = this.format; this.isPrimitive = isPrimitiveType(schema, this.type); this.default = schema.default; this.readOnly = !!schema.readOnly; this.writeOnly = !!schema.writeOnly; + if (!!schema.nullable) { + if (Array.isArray(this.type)) this.type.push('null'); + else this.type = [this.type, 'null']; + } + + this.displayType = Array.isArray(this.type) ? this.type.join(' or ') : this.type; + if (this.isCircular) { return; } @@ -144,8 +144,7 @@ export class SchemaModel { } if (schema.oneOf !== undefined) { - this.nullable = this.nullable || schema.oneOf.some(s => s.type === 'null'); - this.initOneOf(schema.oneOf.filter(s => s.type !== 'null'), parser); + this.initOneOf(schema.oneOf, parser); this.oneOfType = 'One of'; if (schema.anyOf !== undefined) { console.warn( @@ -156,8 +155,7 @@ export class SchemaModel { } if (schema.anyOf !== undefined) { - this.nullable = this.nullable || schema.anyOf.some(s => s.type === 'null'); - this.initOneOf(schema.anyOf.filter(s => s.type !== 'null'), parser); + this.initOneOf(schema.anyOf, parser); this.oneOfType = 'Any of'; return; } diff --git a/src/types/open-api.d.ts b/src/types/open-api.d.ts index 7134890ea0..f235632672 100644 --- a/src/types/open-api.d.ts +++ b/src/types/open-api.d.ts @@ -10,6 +10,7 @@ export interface OpenAPISpec { tags?: OpenAPITag[]; externalDocs?: OpenAPIExternalDocumentation; 'x-webhooks'?: OpenAPIPaths; + webhooks?: OpenAPIPaths; } export interface OpenAPIInfo { @@ -107,7 +108,7 @@ export interface OpenAPIExample { export interface OpenAPISchema { $ref?: string; - type?: string; + type?: string | string[]; properties?: { [name: string]: OpenAPISchema }; additionalProperties?: boolean | OpenAPISchema; description?: string; diff --git a/src/utils/__tests__/openapi.test.ts b/src/utils/__tests__/openapi.test.ts index 7050f36f16..2045c49e49 100644 --- a/src/utils/__tests__/openapi.test.ts +++ b/src/utils/__tests__/openapi.test.ts @@ -174,6 +174,20 @@ describe('Utils', () => { expect(isPrimitiveType(schema)).toEqual(false); }); + it('Should return false for array of strings', () => { + const schema = { + type: ['object', 'string'], + }; + expect(isPrimitiveType(schema)).toEqual(false); + }); + + it('Should return false for array of string which include the null value', () => { + const schema = { + type: ['object', 'string', 'null'], + }; + expect(isPrimitiveType(schema)).toEqual(false); + }); + it('Should return false for array with non-empty objects', () => { const schema = { type: 'array', diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 71af2572be..ff9c6afd25 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -96,7 +96,7 @@ const schemaKeywordTypes = { }; export function detectType(schema: OpenAPISchema): string { - if (schema.type !== undefined) { + if (schema.type !== undefined && !Array.isArray(schema.type)) { return schema.type; } const keywords = Object.keys(schemaKeywordTypes); @@ -110,7 +110,7 @@ export function detectType(schema: OpenAPISchema): string { return 'any'; } -export function isPrimitiveType(schema: OpenAPISchema, type: string | undefined = schema.type) { +export function isPrimitiveType(schema: OpenAPISchema, type: string | string[] | undefined = schema.type) { if (schema.oneOf !== undefined || schema.anyOf !== undefined) { return false; } @@ -128,6 +128,8 @@ export function isPrimitiveType(schema: OpenAPISchema, type: string | undefined return false; } + if (Array.isArray(type)) return false + return true; }