Skip to content

Commit

Permalink
refactor: improve internal code (#614)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Sep 15, 2022
1 parent 820fae3 commit 7b4554b
Show file tree
Hide file tree
Showing 36 changed files with 436 additions and 475 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ import { Parser, fromURL } from '@asyncapi/parser';

const parser = new Parser();

const { document, diagnostics } = await (await fromURL(parser, 'https://example.com/')).parse();
const { document, diagnostics } = await fromURL(parser, 'https://example.com/').parse();
```

### Example with performing actions on file source
Expand All @@ -160,7 +160,7 @@ import { Parser, fromFile } from '@asyncapi/parser';

const parser = new Parser();

const { document, diagnostics } = await (await fromFile(parser, './asyncapi.yaml')).parse();
const { document, diagnostics } = await fromFile(parser, './asyncapi.yaml').parse();
```

### [Example with stringify and unstringify parsed document](#stringify)
Expand Down
16 changes: 14 additions & 2 deletions src/document.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { newAsyncAPIDocument, AsyncAPIDocumentV2, AsyncAPIDocumentV3 } from './models';
import { AsyncAPIDocumentV2, AsyncAPIDocumentV3 } from './models';
import { unstringify } from './stringify';
import { createDetailedAsyncAPI } from './utils';

Expand All @@ -8,6 +8,18 @@ import {
} from './constants';

import type { AsyncAPIDocumentInterface } from './models';
import type { DetailedAsyncAPI } from './types';

export function createAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface {
switch (asyncapi.semver.major) {
case 2:
return new AsyncAPIDocumentV2(asyncapi.parsed, { asyncapi, pointer: '/' });
// case 3:
// return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' });
default:
throw new Error(`Unsupported AsyncAPI version: ${asyncapi.semver.version}`);
}
}

export function toAsyncAPIDocument(maybeDoc: unknown): AsyncAPIDocumentInterface | undefined {
if (isAsyncAPIDocument(maybeDoc)) {
Expand All @@ -16,7 +28,7 @@ export function toAsyncAPIDocument(maybeDoc: unknown): AsyncAPIDocumentInterface
if (!isParsedDocument(maybeDoc)) {
return;
}
return unstringify(maybeDoc) || newAsyncAPIDocument(createDetailedAsyncAPI(maybeDoc, maybeDoc as any));
return unstringify(maybeDoc) || createAsyncAPIDocument(createDetailedAsyncAPI(maybeDoc, maybeDoc as any));
}

export function isAsyncAPIDocument(maybeDoc: unknown): maybeDoc is AsyncAPIDocumentInterface {
Expand Down
11 changes: 6 additions & 5 deletions src/from.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { readFile } from 'fs/promises';

import type { RequestInit } from 'node-fetch';
import type { Parser } from './parser';
import type { ParseInput, ParseOptions, ParseOutput } from './parse';
import type { ValidateOptions, ValidateOutput } from './lint';
import type { ParseOptions, ParseOutput } from './parse';
import type { ValidateOptions } from './validate';
import type { Input, Diagnostic } from './types';

interface FromResult {
parse: (options?: ParseOptions) => Promise<ParseOutput>;
validate: (options?: ValidateOptions) => Promise<ValidateOutput>;
validate: (options?: ValidateOptions) => Promise<Diagnostic[]>;
}

export function fromURL(parser: Parser, source: string, options?: RequestInit): FromResult {
async function fetchUrl(): Promise<ParseInput> {
async function fetchUrl(): Promise<Input> {
const fetchFn = await getFetch();
return (await fetchFn(source, options as any)).text();
}
Expand All @@ -29,7 +30,7 @@ export function fromURL(parser: Parser, source: string, options?: RequestInit):
}

export function fromFile(parser: Parser, source: string, options?: Parameters<typeof readFile>[1]): FromResult {
async function readFileFn(): Promise<ParseInput> {
async function readFileFn(): Promise<Input> {
return (await readFile(source, options)).toString();
}

Expand Down
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export { fromURL, fromFile } from './from';
export { AsyncAPIDocument as OldAsyncAPIDocument } from './old-api/asyncapi';
export { migrateToOldAPI } from './old-api/migrator';

export type { AsyncAPISemver, Diagnostic, SchemaValidateResult } from './types';
export type { LintOptions, ValidateOptions, ValidateOutput } from './lint';
export type { ParseInput, ParseOptions, ParseOutput } from './parse';
export type { AsyncAPISemver, Input, Diagnostic, SchemaValidateResult } from './types';
export type { ValidateOptions, ValidateOutput } from './validate';
export type { ParseOptions, ParseOutput } from './parse';
export type { StringifyOptions } from './stringify';
export type { ValidateSchemaInput, ParseSchemaInput, SchemaParser } from './schema-parser';

Expand Down
73 changes: 0 additions & 73 deletions src/lint.ts

This file was deleted.

14 changes: 0 additions & 14 deletions src/models/asyncapi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { AsyncAPIDocumentV2 } from './v2';

import type { BaseModel } from './base';
import type { InfoInterface } from './info';
import type { ChannelsInterface } from './channels';
Expand All @@ -10,7 +8,6 @@ import type { OperationsInterface } from './operations';
import type { SchemasInterface } from './schemas';
import type { SecuritySchemesInterface } from './security-schemes';
import type { ServersInterface } from './servers';
import type { DetailedAsyncAPI } from '../types';

import type { v2 } from '../spec-types';

Expand All @@ -27,14 +24,3 @@ export interface AsyncAPIDocumentInterface extends BaseModel<v2.AsyncAPIObject>,
securitySchemes(): SecuritySchemesInterface;
components(): ComponentsInterface;
}

export function newAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface {
switch (asyncapi.semver.major) {
case 2:
return new AsyncAPIDocumentV2(asyncapi.parsed, { asyncapi, pointer: '/' });
// case 3:
// return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' });
default:
throw new Error(`Unsupported AsyncAPI version: ${asyncapi.semver.version}`);
}
}
88 changes: 28 additions & 60 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { AsyncAPIDocumentInterface, newAsyncAPIDocument } from './models';
import { AsyncAPIDocumentInterface } from './models';

import { customOperations } from './custom-operations';
import { validate } from './lint';
import { validate } from './validate';
import { copy } from './stringify';
import { toAsyncAPIDocument } from './document';
import { createDetailedAsyncAPI, normalizeInput } from './utils';
import { createAsyncAPIDocument } from './document';
import { createDetailedAsyncAPI, mergePatch, setExtension } from './utils';

import { xParserSpecParsed } from './constants';

import type { Spectral } from '@stoplight/spectral-core';
import type { Parser } from './parser';
import type { ValidateOptions } from './lint';
import type { MaybeAsyncAPI, Diagnostic } from './types';
import type { ValidateOptions } from './validate';
import type { Input, Diagnostic } from './types';

export type ParseInput = string | MaybeAsyncAPI | AsyncAPIDocumentInterface;
export interface ParseOutput {
document: AsyncAPIDocumentInterface | undefined;
diagnostics: Diagnostic[];
Expand All @@ -25,64 +25,32 @@ export interface ParseOptions {
validateOptions?: Omit<ValidateOptions, 'source'>;
}

export async function parse(parser: Parser, asyncapi: ParseInput, options?: ParseOptions): Promise<ParseOutput> {
const maybeDocument = toAsyncAPIDocument(asyncapi);
if (maybeDocument) {
return {
document: maybeDocument,
diagnostics: [],
};
}

try {
const document = normalizeInput(asyncapi as Exclude<ParseInput, AsyncAPIDocumentInterface>);
options = normalizeOptions(options);

const { validated, diagnostics } = await validate(parser, document, { ...options.validateOptions, source: options.source });
if (validated === undefined) {
return {
document: undefined,
diagnostics,
};
}

// unfreeze the object - Spectral makes resolved document "freezed"
const validatedDoc = copy(validated as Record<string, any>);
validatedDoc[String(xParserSpecParsed)] = true;

const detailed = createDetailedAsyncAPI(asyncapi as string | Record<string, unknown>, validatedDoc);
const parsedDoc = newAsyncAPIDocument(detailed);
await customOperations(parser, parsedDoc, detailed, options);

return {
document: parsedDoc,
diagnostics,
};
} catch (err: any) {
// TODO: throw proper error
throw new Error(err.message);
}
}

const defaultOptions: ParseOptions = {
applyTraits: true,
parseSchemas: true,
validateOptions: {},
};
function normalizeOptions(options?: ParseOptions): ParseOptions {
if (!options || typeof options !== 'object') {
return defaultOptions;
}
// shall copy
options = { ...defaultOptions, ...options };

// applyTraits
if (options.applyTraits === undefined) {
options.applyTraits = true;
}
// parseSchemas
if (options.parseSchemas === undefined) {
options.parseSchemas = true;
export async function parse(parser: Parser, spectral: Spectral, asyncapi: Input, options: ParseOptions = {}): Promise<ParseOutput> {
options = mergePatch<ParseOptions>(defaultOptions, options);
const { validated, diagnostics } = await validate(spectral, asyncapi, { ...options.validateOptions, source: options.source });
if (validated === undefined) {
return {
document: undefined,
diagnostics,
};
}

return options;
// unfreeze the object - Spectral makes resolved document "freezed"
const validatedDoc = copy(validated as Record<string, any>);

const detailed = createDetailedAsyncAPI(asyncapi as string | Record<string, unknown>, validatedDoc);
const document = createAsyncAPIDocument(detailed);
setExtension(xParserSpecParsed, true, document);
await customOperations(parser, document, detailed, options);

return {
document,
diagnostics,
};
}
51 changes: 25 additions & 26 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import { Spectral } from '@stoplight/spectral-core';

import { toAsyncAPIDocument } from './document';
import { parse } from './parse';
import { lint, validate } from './lint';
import { validate } from './validate';
import { registerSchemaParser } from './schema-parser';
import { AsyncAPISchemaParser } from './schema-parser/asyncapi-schema-parser';
import { configureSpectral } from './spectral';
import { createSpectral } from './spectral';

import type { IConstructorOpts } from '@stoplight/spectral-core';
import type { ParseInput, ParseOptions } from './parse';
import type { LintOptions, ValidateOptions } from './lint';
import type { ParseOptions, ParseOutput } from './parse';
import type { ValidateOptions } from './validate';
import type { SchemaParser } from './schema-parser';
import type { Diagnostic, Input } from './types';

export interface ParserOptions {
spectral?: Spectral | IConstructorOpts;
}
export interface ParserOptions {}

export class Parser {
public readonly parserRegistry = new Map<string, SchemaParser>();
public readonly spectral: Spectral;
protected readonly spectral: Spectral;

constructor(
private readonly options?: ParserOptions
private readonly _: ParserOptions = {}
) {
const { spectral } = this.options || {};
if (spectral instanceof Spectral) {
this.spectral = spectral;
} else {
this.spectral = new Spectral(spectral);
}

this.spectral = createSpectral(this);
this.registerSchemaParser(AsyncAPISchemaParser());
configureSpectral(this);
}

parse(asyncapi: ParseInput, options?: ParseOptions) {
return parse(this, asyncapi, options);
}

lint(asyncapi: ParseInput, options?: LintOptions) {
return lint(this, asyncapi, options);
async parse(asyncapi: Input, options?: ParseOptions): Promise<ParseOutput> {
const maybeDocument = toAsyncAPIDocument(asyncapi);
if (maybeDocument) {
return {
document: maybeDocument,
diagnostics: [],
};
}
return parse(this, this.spectral, asyncapi, options);
}

validate(asyncapi: ParseInput, options?: ValidateOptions) {
return validate(this, asyncapi, options);
async validate(asyncapi: Input, options?: ValidateOptions): Promise<Diagnostic[]> {
const maybeDocument = toAsyncAPIDocument(asyncapi);
if (maybeDocument) {
return [];
}
return (await validate(this.spectral, asyncapi, options)).diagnostics;
}

registerSchemaParser(parser: SchemaParser) {
Expand Down
Loading

0 comments on commit 7b4554b

Please sign in to comment.