diff --git a/library/src/containers/AsyncApi/AsyncApi.tsx b/library/src/containers/AsyncApi/AsyncApi.tsx deleted file mode 100644 index dd3d37d1..00000000 --- a/library/src/containers/AsyncApi/AsyncApi.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { Component } from 'react'; -import { AsyncAPIDocumentInterface } from '@asyncapi/parser'; - -import AsyncApiStandalone from './Standalone'; - -import { - isFetchingSchemaInterface, - ErrorObject, - PropsSchema, -} from '../../types'; -import { ConfigInterface } from '../../config'; -import { SpecificationHelpers, Parser } from '../../helpers'; - -export interface AsyncApiProps { - schema: PropsSchema; - config?: Partial; -} - -interface AsyncAPIState { - asyncapi?: AsyncAPIDocumentInterface; - error?: ErrorObject; -} - -class AsyncApiComponent extends Component { - state: AsyncAPIState = { - asyncapi: undefined, - error: undefined, - }; - - async componentDidMount() { - if (this.props.schema) { - const { schema, config } = this.props; - await this.parseSchema(schema, config?.parserOptions); - } - } - - async componentDidUpdate(prevProps: AsyncApiProps) { - const oldSchema = prevProps.schema; - const newSchema = this.props.schema; - - if (oldSchema !== newSchema) { - const { config } = this.props; - await this.parseSchema(newSchema, config?.parserOptions); - } - } - - render() { - const { schema, config } = this.props; - const { asyncapi, error } = this.state; - - return ( - - ); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private async parseSchema(schema: PropsSchema, parserOptions?: any) { - const parsedSpec = SpecificationHelpers.retrieveParsedSpec(schema); - if (parsedSpec) { - this.setState({ - asyncapi: parsedSpec, - }); - return; - } - - if (isFetchingSchemaInterface(schema)) { - const parsedFromUrl = await Parser.parseFromUrl(schema, parserOptions); - this.setState({ - asyncapi: parsedFromUrl.asyncapi, - error: parsedFromUrl.error, - }); - return; - } - - const parsed = await Parser.parse(schema, parserOptions); - this.setState({ - asyncapi: parsed.asyncapi, - error: parsed.error, - }); - } -} - -export default AsyncApiComponent; diff --git a/library/src/containers/AsyncApi/Standalone.tsx b/library/src/containers/AsyncApi/Standalone.tsx index 9c5cc3c8..6db0c853 100644 --- a/library/src/containers/AsyncApi/Standalone.tsx +++ b/library/src/containers/AsyncApi/Standalone.tsx @@ -1,8 +1,12 @@ import React, { Component } from 'react'; import { AsyncAPIDocumentInterface } from '@asyncapi/parser'; -import { SpecificationHelpers } from '../../helpers'; -import { ErrorObject, PropsSchema } from '../../types'; +import { Parser, SpecificationHelpers } from '../../helpers'; +import { + ErrorObject, + isFetchingSchemaInterface, + PropsSchema, +} from '../../types'; import { ConfigInterface, defaultConfig } from '../../config'; import AsyncApiLayout from './Layout'; @@ -11,7 +15,6 @@ import { Error } from '../Error/Error'; export interface AsyncApiProps { schema: PropsSchema; config?: Partial; - error?: ErrorObject; } interface AsyncAPIState { @@ -27,33 +30,27 @@ class AsyncApiComponent extends Component { constructor(props: AsyncApiProps) { super(props); - - const parsedSpec = SpecificationHelpers.retrieveParsedSpec(props.schema); - if (parsedSpec) { - this.state = { asyncapi: parsedSpec }; - } } - componentDidMount() { + componentDidMount = async () => { if (!this.state.asyncapi) { - this.updateState(this.props.schema); + await this.parseSchemaAndSetState(this.props.schema); } - } + }; - componentDidUpdate(prevProps: AsyncApiProps) { + componentDidUpdate = async (prevProps: AsyncApiProps) => { const oldSchema = prevProps.schema; const newSchema = this.props.schema; if (oldSchema !== newSchema) { - this.updateState(newSchema); + await this.parseSchemaAndSetState(newSchema); } - } + }; render() { - const { config, error: propError } = this.props; - const { asyncapi, error: stateError } = this.state; + const { config } = this.props; + const { asyncapi, error } = this.state; - const error = propError ?? stateError; const concatenatedConfig: ConfigInterface = { ...defaultConfig, ...config, @@ -93,13 +90,34 @@ class AsyncApiComponent extends Component { ); } - private updateState(schema: PropsSchema) { + private async parseSchemaAndSetState( + schema: PropsSchema, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + parserOptions?: any, + ) { const parsedSpec = SpecificationHelpers.retrieveParsedSpec(schema); - if (!parsedSpec) { - this.setState({ asyncapi: undefined }); + if (parsedSpec) { + this.setState({ + asyncapi: parsedSpec, + error: undefined, + }); + return; + } + + if (isFetchingSchemaInterface(schema)) { + const parsedFromUrl = await Parser.parseFromUrl(schema, parserOptions); + this.setState({ + asyncapi: parsedFromUrl.asyncapi, + error: parsedFromUrl.error, + }); return; } - this.setState({ asyncapi: parsedSpec }); + + const parsed = await Parser.parse(schema, parserOptions); + this.setState({ + asyncapi: parsed.asyncapi, + error: parsed.error, + }); } } diff --git a/library/src/helpers/parser.ts b/library/src/helpers/parser.ts index 0ecb3cc2..d5cf3fe6 100644 --- a/library/src/helpers/parser.ts +++ b/library/src/helpers/parser.ts @@ -1,4 +1,9 @@ -import { Parser as AsyncapiParser, fromURL } from '@asyncapi/parser'; +import { + Parser as AsyncapiParser, + Diagnostic, + DiagnosticSeverity, + fromURL, +} from '@asyncapi/parser'; import { OpenAPISchemaParser } from '@asyncapi/openapi-schema-parser'; import { ProtoBuffSchemaParser } from '@asyncapi/protobuf-schema-parser'; import { AvroSchemaParser } from '@asyncapi/avro-schema-parser'; @@ -20,47 +25,24 @@ asyncapiParser.registerSchemaParser(ProtoBuffSchemaParser()); // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class Parser { static async parse( - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents, @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument content: string | any, // eslint-disable-next-line @typescript-eslint/no-explicit-any parserOptions?: any, ): Promise { try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const parseResult = await asyncapiParser.parse(content, parserOptions); - - const error: { - title: string | undefined; - validationErrors: ValidationError[] | undefined; - } = { - title: 'There are errors in your Asyncapi document', - validationErrors: [], - }; + const { document, diagnostics } = await asyncapiParser.parse( + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + content, + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + parserOptions, + ); - if (parseResult.document === undefined) { - parseResult.diagnostics.forEach((diagnostic) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison - if (diagnostic.severity == 0) { - const tempObj: ValidationError = { - title: diagnostic.message, - location: { - jsonPointer: '/' + diagnostic.path.join('/'), - startLine: diagnostic.range.start.line, - startColumn: diagnostic.range.start.character, - // as of @asyncapi/parser 3.3.0 offset of 1 correctly shows the error line - startOffset: 1, - endLine: diagnostic.range.end.line, - endColumn: diagnostic.range.end.character, - endOffset: 0, - }, - }; - error.validationErrors?.push(tempObj); - } - }); - throw error; + if (document === undefined) { + throw this.convertDiagnosticToErrorObject(diagnostics, [0]); } - return { asyncapi: parseResult.document }; + return { asyncapi: document }; } catch (err) { return this.handleError(err as ErrorObject); } @@ -78,14 +60,53 @@ export class Parser { // eslint-disable-next-line @typescript-eslint/no-explicit-any arg.requestOptions as any, ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const { document } = await fromResult.parse(parserOptions); - return { asyncapi: document }; + const { document, diagnostics } = await fromResult.parse(parserOptions); + + if (document == undefined) { + // this means there are errors in the document. + // so we gather all the severity 0 diagnostics and throw them as errors + throw this.convertDiagnosticToErrorObject(diagnostics, [0]); + } + + return { asyncapi: document, error: undefined }; } catch (err) { return this.handleError(err as ErrorObject); } } + static readonly convertDiagnosticToErrorObject = ( + diagnostics: Diagnostic[], + severities: DiagnosticSeverity[], + ): ErrorObject => { + const error: ErrorObject = { + title: 'There are errors in your Asyncapi document', + type: 'VALIDATION_ERRORS_TYPE', + validationErrors: [], + }; + diagnostics.forEach((diagnostic) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison + if (severities.includes(diagnostic.severity)) { + const tempObj: ValidationError = { + title: diagnostic.message, + location: { + jsonPointer: '/' + diagnostic.path.join('/'), + startLine: diagnostic.range.start.line, + startColumn: diagnostic.range.start.character, + // as of @asyncapi/parser 3.3.0 offset of 1 correctly shows the error line + startOffset: 1, + endLine: diagnostic.range.end.line, + endColumn: diagnostic.range.end.character, + endOffset: 0, + }, + }; + error.validationErrors?.push(tempObj); + } + }); + return error; + }; + private static handleError = (err: ErrorObject): ParserReturn => { if (err.type === VALIDATION_ERRORS_TYPE) { return { diff --git a/library/src/index.ts b/library/src/index.ts index f5fc0a8c..eaaa76f6 100644 --- a/library/src/index.ts +++ b/library/src/index.ts @@ -1,7 +1,7 @@ -import AsyncApiComponent from './containers/AsyncApi/AsyncApi'; +import AsyncApiComponent from './containers/AsyncApi/Standalone'; import AsyncApiComponentWP from './containers/AsyncApi/Standalone'; -export { AsyncApiProps } from './containers/AsyncApi/AsyncApi'; +export { AsyncApiProps } from './containers/AsyncApi/Standalone'; export { ConfigInterface } from './config/config'; export { FetchingSchemaInterface } from './types'; diff --git a/library/src/standalone.ts b/library/src/standalone.ts index 5ed62e13..63bbc381 100644 --- a/library/src/standalone.ts +++ b/library/src/standalone.ts @@ -1,6 +1,6 @@ import AsyncApiComponent, { AsyncApiProps, -} from './containers/AsyncApi/AsyncApi'; +} from './containers/AsyncApi/Standalone'; import { createRender, createHydrate } from './standalone-codebase'; import { hljs } from './helpers';