From 18b13b4e8893c5c678b083348653421d8d5b7ec7 Mon Sep 17 00:00:00 2001 From: Alex Varchuk Date: Fri, 6 May 2022 19:01:47 +0300 Subject: [PATCH] chore: add isArray helper function --- src/components/Schema/Schema.tsx | 3 ++- src/services/OpenAPIParser.ts | 4 ++-- src/services/RedocNormalizedOptions.ts | 4 ++-- src/services/models/Schema.ts | 20 +++++++++---------- src/utils/helpers.ts | 6 +++++- src/utils/openapi.ts | 27 ++++++++++++++------------ 6 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/components/Schema/Schema.tsx b/src/components/Schema/Schema.tsx index 4657fbb2..badd2abe 100644 --- a/src/components/Schema/Schema.tsx +++ b/src/components/Schema/Schema.tsx @@ -11,6 +11,7 @@ import { ObjectSchema } from './ObjectSchema'; import { OneOfSchema } from './OneOfSchema'; import { l } from '../../services/Labels'; +import { isArray } from '../../utils/helpers'; export interface SchemaOptions { showTitle?: boolean; @@ -68,7 +69,7 @@ export class Schema extends React.Component> { return ; } - const types = Array.isArray(type) ? type : [type]; + const types = isArray(type) ? type : [type]; if (types.includes('object')) { if (schema.fields?.length) { return ; diff --git a/src/services/OpenAPIParser.ts b/src/services/OpenAPIParser.ts index bdc86bbe..525cdf6b 100644 --- a/src/services/OpenAPIParser.ts +++ b/src/services/OpenAPIParser.ts @@ -2,7 +2,7 @@ import { resolve as urlResolve } from 'url'; import { OpenAPIRef, OpenAPISchema, OpenAPISpec, Referenced } from '../types'; -import { appendToMdHeading, IS_BROWSER } from '../utils/'; +import { appendToMdHeading, isArray, IS_BROWSER } from '../utils/'; import { JsonPointer } from '../utils/JsonPointer'; import { getDefinitionName, @@ -365,7 +365,7 @@ export class OpenAPIParser { const allOf = schema.allOf; for (let i = 0; i < allOf.length; i++) { const sub = allOf[i]; - if (Array.isArray(sub.oneOf)) { + if (isArray(sub.oneOf)) { const beforeAllOf = allOf.slice(0, i); const afterAllOf = allOf.slice(i + 1); return { diff --git a/src/services/RedocNormalizedOptions.ts b/src/services/RedocNormalizedOptions.ts index b787e407..30f685a1 100644 --- a/src/services/RedocNormalizedOptions.ts +++ b/src/services/RedocNormalizedOptions.ts @@ -1,6 +1,6 @@ import defaultTheme, { ResolvedThemeInterface, resolveTheme, ThemeInterface } from '../theme'; import { querySelector } from '../utils/dom'; -import { isNumeric, mergeObjects } from '../utils/helpers'; +import { isArray, isNumeric, mergeObjects } from '../utils/helpers'; import { LabelsConfigRaw, setRedocLabels } from './Labels'; import { MDXComponentMeta } from './MarkdownRenderer'; @@ -316,7 +316,7 @@ export class RedocNormalizedOptions { this.expandDefaultServerVariables = argValueToBoolean(raw.expandDefaultServerVariables); this.maxDisplayedEnumValues = argValueToNumber(raw.maxDisplayedEnumValues); - const ignoreNamedSchemas = Array.isArray(raw.ignoreNamedSchemas) + const ignoreNamedSchemas = isArray(raw.ignoreNamedSchemas) ? raw.ignoreNamedSchemas : raw.ignoreNamedSchemas?.split(',').map(s => s.trim()); this.ignoreNamedSchemas = new Set(ignoreNamedSchemas); diff --git a/src/services/models/Schema.ts b/src/services/models/Schema.ts index 7b2720da..a0a3b38c 100644 --- a/src/services/models/Schema.ts +++ b/src/services/models/Schema.ts @@ -11,6 +11,7 @@ import { detectType, extractExtensions, humanizeConstraints, + isArray, isNamedDefinition, isPrimitiveType, JsonPointer, @@ -103,7 +104,7 @@ export class SchemaModel { } hasType(type: string) { - return this.type === type || (Array.isArray(this.type) && this.type.includes(type)); + return this.type === type || (isArray(this.type) && this.type.includes(type)); } init(parser: OpenAPIParser, isChild: boolean) { @@ -134,17 +135,14 @@ export class SchemaModel { this.maxItems = schema.maxItems; if (!!schema.nullable || schema['x-nullable']) { - if ( - Array.isArray(this.type) && - !this.type.some(value => value === null || value === 'null') - ) { + if (isArray(this.type) && !this.type.some(value => value === null || value === 'null')) { this.type = [...this.type, 'null']; - } else if (!Array.isArray(this.type) && (this.type !== null || this.type !== 'null')) { + } else if (!isArray(this.type) && (this.type !== null || this.type !== 'null')) { this.type = [this.type, 'null']; } } - this.displayType = Array.isArray(this.type) + this.displayType = isArray(this.type) ? this.type.map(item => (item === null ? 'null' : item)).join(' or ') : this.type; @@ -157,7 +155,7 @@ export class SchemaModel { return; } else if ( isChild && - Array.isArray(schema.oneOf) && + isArray(schema.oneOf) && schema.oneOf.find(s => s.$ref === this.pointer) ) { // we hit allOf of the schema with the parent discriminator @@ -196,7 +194,7 @@ export class SchemaModel { if (this.items.isPrimitive) { this.enum = this.items.enum; } - if (Array.isArray(this.type)) { + if (isArray(this.type)) { const filteredType = this.type.filter(item => item !== 'array'); if (filteredType.length) this.displayType += ` or ${filteredType.join(' or ')}`; } @@ -295,7 +293,7 @@ export class SchemaModel { for (const name in mapping) { const $ref = mapping[name]; - if (Array.isArray(explicitInversedMapping[$ref])) { + if (isArray(explicitInversedMapping[$ref])) { explicitInversedMapping[$ref].push(name); } else { // overrides implicit mapping here @@ -311,7 +309,7 @@ export class SchemaModel { for (const $ref of Object.keys(inversedMapping)) { const names = inversedMapping[$ref]; - if (Array.isArray(names)) { + if (isArray(names)) { for (const name of names) { refs.push({ $ref, name }); } diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 2b7ea8e8..617d97ac 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -113,7 +113,7 @@ const isObject = (item: any): boolean => { }; const isMergebleObject = (item): boolean => { - return isObject(item) && !Array.isArray(item); + return isObject(item) && !isArray(item); }; /** @@ -205,3 +205,7 @@ export function unescapeHTMLChars(str: string): string { .replace(/&/g, '&') .replace(/"/g, '"'); } + +export function isArray(value: unknown): value is Array { + return Array.isArray(value); +} diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index cedb9053..e8a0cc05 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -16,7 +16,7 @@ import { Referenced, } from '../types'; import { IS_BROWSER } from './dom'; -import { isNumeric, removeQueryString, resolveUrl } from './helpers'; +import { isNumeric, removeQueryString, resolveUrl, isArray } from './helpers'; function isWildcardStatusCode(statusCode: string | number): statusCode is string { return typeof statusCode === 'string' && /\dxx/i.test(statusCode); @@ -102,7 +102,7 @@ const schemaKeywordTypes = { }; export function detectType(schema: OpenAPISchema): string { - if (schema.type !== undefined && !Array.isArray(schema.type)) { + if (schema.type !== undefined && !isArray(schema.type)) { return schema.type; } const keywords = Object.keys(schemaKeywordTypes); @@ -125,16 +125,19 @@ export function isPrimitiveType( } let isPrimitive = true; - const isArray = Array.isArray(type); + const isArrayType = isArray(type); - if (type === 'object' || (isArray && type?.includes('object'))) { + if (type === 'object' || (isArrayType && type?.includes('object'))) { isPrimitive = schema.properties !== undefined ? Object.keys(schema.properties).length === 0 : schema.additionalProperties === undefined && schema.unevaluatedProperties === undefined; } - if (schema.items !== undefined && (type === 'array' || (isArray && type?.includes('array')))) { + if ( + schema.items !== undefined && + (type === 'array' || (isArrayType && type?.includes('array'))) + ) { isPrimitive = isPrimitiveType(schema.items, schema.items.type); } @@ -150,7 +153,7 @@ export function isFormUrlEncoded(contentType: string): boolean { } function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: string): string { - if (Array.isArray(fieldVal)) { + if (isArray(fieldVal)) { return fieldVal.map(v => v.toString()).join(delimiter); } else if (typeof fieldVal === 'object') { return Object.keys(fieldVal) @@ -162,7 +165,7 @@ function delimitedEncodeField(fieldVal: any, fieldName: string, delimiter: strin } function deepObjectEncodeField(fieldVal: any, fieldName: string): string { - if (Array.isArray(fieldVal)) { + if (isArray(fieldVal)) { console.warn('deepObject style cannot be used with array value:' + fieldVal.toString()); return ''; } else if (typeof fieldVal === 'object') { @@ -195,7 +198,7 @@ export function urlFormEncodePayload( payload: object, encoding: { [field: string]: OpenAPIEncoding } = {}, ) { - if (Array.isArray(payload)) { + if (isArray(payload)) { throw new Error('Payload must have fields: ' + payload.toString()); } else { return Object.keys(payload) @@ -254,7 +257,7 @@ function serializeQueryParameter( case 'form': return serializeFormValue(name, explode, value); case 'spaceDelimited': - if (!Array.isArray(value)) { + if (!isArray(value)) { console.warn('The style spaceDelimited is only applicable to arrays'); return ''; } @@ -264,7 +267,7 @@ function serializeQueryParameter( return `${name}=${value.join('%20')}`; case 'pipeDelimited': - if (!Array.isArray(value)) { + if (!isArray(value)) { console.warn('The style pipeDelimited is only applicable to arrays'); return ''; } @@ -274,7 +277,7 @@ function serializeQueryParameter( return `${name}=${value.join('|')}`; case 'deepObject': - if (!explode || Array.isArray(value) || typeof value !== 'object') { + if (!explode || isArray(value) || typeof value !== 'object') { console.warn('The style deepObject is only applicable for objects with explode=true'); return ''; } @@ -330,7 +333,7 @@ export function serializeParameterValueWithMime(value: any, mime: string): strin } export function serializeParameterValue( - parameter: OpenAPIParameter & { serializationMime?: string }, + parameter: (OpenAPIParameter & { serializationMime?: string }) | FieldModel, value: any, ): string { const { name, style, explode = false, serializationMime } = parameter;