diff --git a/src/custom-operations/apply-traits.ts b/src/custom-operations/apply-traits.ts index 210668994..97f2bfd1b 100644 --- a/src/custom-operations/apply-traits.ts +++ b/src/custom-operations/apply-traits.ts @@ -3,6 +3,8 @@ import { JSONPath } from 'jsonpath-plus'; import { xParserOriginalTraits } from '../constants'; import { mergePatch } from '../utils'; +import type { v2 } from "../spec-types"; + const v2TraitPaths = [ // operations '$.channels.*.[publish,subscribe]', @@ -15,7 +17,7 @@ const v2TraitPaths = [ '$.components.messages.*', ]; -export function applyTraitsV2(asyncapi: Record) { +export function applyTraitsV2(asyncapi: v2.AsyncAPIObject) { applyAllTraits(asyncapi, v2TraitPaths); } @@ -31,11 +33,11 @@ const v3TraitPaths = [ '$.components.messages.*', ]; -export function applyTraitsV3(asyncapi: Record) { +export function applyTraitsV3(asyncapi: v2.AsyncAPIObject) { // TODO: Change type when we will have implemented types for v3 applyAllTraits(asyncapi, v3TraitPaths); } -function applyAllTraits(asyncapi: Record, paths: string[]) { +function applyAllTraits(asyncapi: Record, paths: string[]) { paths.forEach(path => { JSONPath({ path, diff --git a/src/models/asyncapi.ts b/src/models/asyncapi.ts index e6cf99adf..142acf417 100644 --- a/src/models/asyncapi.ts +++ b/src/models/asyncapi.ts @@ -12,7 +12,9 @@ import type { SecuritySchemesInterface } from "./security-schemes"; import type { ServersInterface } from "./servers"; import type { DetailedAsyncAPI } from "../types"; -export interface AsyncAPIDocumentInterface extends BaseModel, ExtensionsMixinInterface { +import type { v2 } from "../spec-types"; + +export interface AsyncAPIDocumentInterface extends BaseModel, ExtensionsMixinInterface { version(): string; defaultContentType(): string | undefined; hasDefaultContentType(): boolean; diff --git a/src/models/base.ts b/src/models/base.ts index 748a56889..db9323a54 100644 --- a/src/models/base.ts +++ b/src/models/base.ts @@ -7,32 +7,36 @@ export interface ModelMetadata { [key: string]: any; } -export abstract class BaseModel { +export abstract class BaseModel = Record, M extends Record = {}> { constructor( - protected readonly _json: Record, - protected readonly _meta: ModelMetadata = {} as any, + protected readonly _json: J, + protected readonly _meta: ModelMetadata & M = {} as any, ) {} - json>(): T; - json(key: string | number): T; - json(key?: string | number) { + json = J>(): T; + json(key: K): J[K]; + json(key?: keyof J) { if (key === undefined) return this._json; if (!this._json) return; return this._json[String(key)]; } - meta(): ModelMetadata { - return this._meta!; + meta(): ModelMetadata & M; + meta(key: K): (ModelMetadata & M)[K]; + meta(key?: keyof ModelMetadata & M) { + if (key === undefined) return this._meta; + if (!this._meta) return; + return this._meta[String(key)]; } - jsonPath(field?: string): string | undefined { + jsonPath(field?: string | undefined): string { if (typeof field !== 'string') { - return this._meta?.pointer; + return this._meta.pointer; } - return `${this._meta?.pointer}/${field}`; + return `${this._meta.pointer}/${field}`; } - protected createModel(Model: Constructor, value: any, { pointer, ...rest }: { pointer: string | number, [key: string]: any }): T { - return new Model(value, { ...rest, asyncapi: this._meta.asyncapi, pointer }); + protected createModel(Model: Constructor, value: any, meta: { pointer: string | number, [key: string]: any }): T { + return new Model(value, { ...meta, asyncapi: this._meta.asyncapi }); } } diff --git a/src/models/message-example.ts b/src/models/message-example.ts index fe535273f..16867bd7b 100644 --- a/src/models/message-example.ts +++ b/src/models/message-example.ts @@ -3,9 +3,9 @@ import type { ExtensionsMixinInterface } from './mixins'; export interface MessageExampleInterface extends BaseModel, ExtensionsMixinInterface { hasName(): boolean; - name(): string; + name(): string | undefined; hasSummary(): boolean; - summary(): string; + summary(): string | undefined; hasHeaders(): boolean; headers(): Record | undefined; hasPayload(): boolean; diff --git a/src/models/oauth-flow.ts b/src/models/oauth-flow.ts index 12f39223b..050064964 100644 --- a/src/models/oauth-flow.ts +++ b/src/models/oauth-flow.ts @@ -1,7 +1,7 @@ import { BaseModel } from './base'; import { ExtensionsMixinInterface } from './mixins'; -export interface OAuthFlowInterface extends BaseModel, ExtensionsMixinInterface { +export interface OAuthFlowInterface = Record> extends BaseModel, ExtensionsMixinInterface { authorizationUrl(): string | undefined; hasRefreshUrl(): boolean; refreshUrl(): string | undefined; diff --git a/src/models/security-scheme.ts b/src/models/security-scheme.ts index b551f761c..243ddfa4d 100644 --- a/src/models/security-scheme.ts +++ b/src/models/security-scheme.ts @@ -8,10 +8,9 @@ export interface SecuritySchemeInterface extends BaseModel, DescriptionMixinInte id(): string hasBearerFormat(): boolean; bearerFormat(): string | undefined; - openIdConnectUrl(): string; + openIdConnectUrl(): string | undefined; scheme(): string | undefined; flows(): OAuthFlowsInterface | undefined; - scopes(): string[]; type(): SecuritySchemaType; in(): string | undefined; } diff --git a/src/models/server.ts b/src/models/server.ts index 388096bf0..e8e302636 100644 --- a/src/models/server.ts +++ b/src/models/server.ts @@ -10,7 +10,7 @@ export interface ServerInterface extends BaseModel, DescriptionMixinInterface, B id(): string url(): string; protocol(): string; - protocolVersion(): string; + protocolVersion(): string | undefined; hasProtocolVersion(): boolean; channels(): ChannelsInterface; operations(): OperationsInterface; diff --git a/src/models/utils.ts b/src/models/utils.ts index 2da3454f4..e58b5ce07 100644 --- a/src/models/utils.ts +++ b/src/models/utils.ts @@ -33,3 +33,7 @@ function mixin(derivedCtor: any, constructors: any[]): typeof BaseModel { }); return derivedCtor; } + +export function createModel(Model: Constructor, value: any, meta: { pointer: string | number, [key: string]: any }, parent: BaseModel) { + return new Model(value, { ...meta, asyncapi: parent?.meta().asyncapi }); +} \ No newline at end of file diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index 271695bf8..7cf366593 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -11,8 +11,7 @@ import { SecuritySchemes } from "./security-schemes"; import { SecurityScheme } from "./security-scheme"; import { Schemas } from "./schemas"; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; import { tilde } from '../../utils'; @@ -27,8 +26,11 @@ import type { MessagesInterface } from "../messages"; import type { MessageInterface } from "../message"; import type { SchemasInterface } from "../schemas"; import type { SecuritySchemesInterface } from "../security-schemes"; +import type { ExtensionsInterface } from "../extensions"; -export class AsyncAPIDocument extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { +import type { v2 } from "../../spec-types"; + +export class AsyncAPIDocument extends BaseModel implements AsyncAPIDocumentInterface { version(): string { return this._json.asyncapi; } @@ -88,4 +90,8 @@ export class AsyncAPIDocument extends Mixin(BaseModel, ExtensionsMixin) implemen components(): ComponentsInterface { return new Components(this._json.components || {}); } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/binding.ts b/src/models/v2/binding.ts new file mode 100644 index 000000000..c2601bca8 --- /dev/null +++ b/src/models/v2/binding.ts @@ -0,0 +1,28 @@ +import { BaseModel } from "../base"; + +import { extensions } from "./mixins"; + +import type { BindingInterface } from "../binding"; +import type { ExtensionsInterface } from "../extensions"; + +import type { v2 } from "../../spec-types"; + +export class Binding extends BaseModel implements BindingInterface { + protocol(): string { + return this._meta.protocol; + } + + version(): string { + return this._json.bindingVersion || 'latest'; + } + + value = Record>(): T { + const value = { ...this._json }; + delete (value as any).bindingVersion; + return value as unknown as T; + } + + extensions(): ExtensionsInterface { + return extensions(this); + } +} diff --git a/src/models/v2/bindings.ts b/src/models/v2/bindings.ts new file mode 100644 index 000000000..e15981e4d --- /dev/null +++ b/src/models/v2/bindings.ts @@ -0,0 +1,14 @@ +import { Collection } from "../collection"; + +import type { BindingsInterface } from "../bindings"; +import type { BindingInterface } from "../binding"; + +export class Bindings extends Collection implements BindingsInterface { + override get(name: string): BindingInterface | undefined { + return this.collections.find(binding => binding.protocol() === name); + }; + + override has(name: string): boolean { + return this.collections.some(binding => binding.protocol() === name); + }; +} diff --git a/src/models/v2/channel-parameter.ts b/src/models/v2/channel-parameter.ts index 0609b0636..e2a094c23 100644 --- a/src/models/v2/channel-parameter.ts +++ b/src/models/v2/channel-parameter.ts @@ -1,22 +1,15 @@ import { BaseModel } from "../base"; import { Schema } from "./schema"; -import { Mixin } from '../utils'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; +import { hasDescription, description, extensions } from './mixins'; -import type { ModelMetadata } from "../base"; import type { ChannelParameterInterface } from "../channel-parameter"; import type { SchemaInterface } from "../schema"; +import type { ExtensionsInterface } from "../extensions"; -export class ChannelParameter extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements ChannelParameterInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string } = {} as any - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class ChannelParameter extends BaseModel implements ChannelParameterInterface { id(): string { return this._meta.id; } @@ -37,4 +30,16 @@ export class ChannelParameter extends Mixin(BaseModel, DescriptionMixin, Extensi location(): string | undefined { return this._json.location; } + + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/channel.ts b/src/models/v2/channel.ts index e178d1643..28102db42 100644 --- a/src/models/v2/channel.ts +++ b/src/models/v2/channel.ts @@ -7,14 +7,12 @@ import { Operation } from './operation'; import { Servers } from './servers'; import { Server } from './server'; -import { Mixin } from '../utils'; -import { BindingsMixin } from './mixins/bindings'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; +import { bindings, hasDescription, description, extensions } from './mixins'; -import type { ModelMetadata } from "../base"; +import type { BindingsInterface } from "models/bindings"; import type { ChannelInterface } from "../channel"; import type { ChannelParametersInterface } from "../channel-parameters"; +import type { ExtensionsInterface } from "models/extensions"; import type { MessagesInterface } from "../messages"; import type { MessageInterface } from "../message"; import type { OperationsInterface } from "../operations"; @@ -22,14 +20,9 @@ import type { OperationInterface } from "../operation"; import type { ServersInterface } from "../servers"; import type { ServerInterface } from "../server"; -export class Channel extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin) implements ChannelInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string, address: string } = {} as any - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class Channel extends BaseModel implements ChannelInterface { id(): string { return this._meta.id; } @@ -38,6 +31,14 @@ export class Channel extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, E return this._meta.address; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + servers(): ServersInterface { const servers: ServerInterface[] = []; const allowedServers: string[] = this._json.servers || []; @@ -52,8 +53,8 @@ export class Channel extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, E operations(): OperationsInterface { const operations: OperationInterface[] = []; ['publish', 'subscribe'].forEach(operationAction => { - this._json[operationAction] && operations.push( - this.createModel(Operation, this._json[operationAction], { id: operationAction, action: operationAction, pointer: `${this._meta.pointer}/${operationAction}` }), + this._json[operationAction as 'publish' | 'subscribe'] && operations.push( + this.createModel(Operation, this._json[operationAction as 'publish' | 'subscribe'], { id: operationAction, action: operationAction, pointer: `${this._meta.pointer}/${operationAction}` }), ); }); return new Operations(operations); @@ -75,4 +76,12 @@ export class Channel extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, E }) ); } + + bindings(): BindingsInterface { + return bindings(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/components.ts b/src/models/v2/components.ts index 89bf62364..e14b50f81 100644 --- a/src/models/v2/components.ts +++ b/src/models/v2/components.ts @@ -1,4 +1,6 @@ import { BaseModel } from "../base"; +import { Bindings } from "./bindings"; +import { Binding } from "./binding"; import { Channel } from "./channel"; import { ChannelParameter } from "./channel-parameter"; import { CorrelationId } from "./correlation-id"; @@ -10,15 +12,14 @@ import { SecurityScheme } from "./security-scheme"; import { Server } from "./server"; import { ServerVariable } from "./server-variable"; -import { Mixin } from '../utils'; -import { Bindings, Binding } from "./mixins/bindings"; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; import type { BindingsInterface } from "../bindings"; import type { ComponentsInterface } from "../components"; import type { ChannelInterface } from "../channel"; import type { ChannelParameterInterface } from "../channel-parameter"; import type { CorrelationIdInterface } from "../correlation-id"; +import type { ExtensionsInterface } from "../extensions"; import type { MessageInterface } from "../message"; import type { MessageTraitInterface } from "../message-trait"; import type { OperationTraitInterface } from "../operation-trait"; @@ -28,7 +29,9 @@ import type { ServerInterface } from "../server"; import type { ServerVariableInterface } from "../server-variable"; import type { Constructor } from "../utils"; -export class Components extends Mixin(BaseModel, ExtensionsMixin) implements ComponentsInterface { +import type { v2 } from "../../spec-types"; + +export class Components extends BaseModel implements ComponentsInterface { servers(): Record { return this.createMap('servers', Server); } @@ -85,14 +88,18 @@ export class Components extends Mixin(BaseModel, ExtensionsMixin) implements Com return this.createBindings('messageBindings'); } - protected createMap(itemsName: string, model: Constructor): Record { + extensions(): ExtensionsInterface { + return extensions(this); + } + + protected createMap(itemsName: keyof v2.ComponentsObject, model: Constructor): Record { return Object.entries(this._json[itemsName] || {}).reduce((items, [itemName, item]) => { items[itemName] = this.createModel(model, item, { id: itemName, pointer: `/components/${itemsName}/${itemName}` }) return items; }, {} as Record); } - protected createBindings(itemsName: string): Record { + protected createBindings(itemsName: 'serverBindings' | 'channelBindings' | 'operationBindings' | 'messageBindings'): Record { return Object.entries(this._json[itemsName] || {}).reduce((bindings, [name, item]) => { bindings[name] = new Bindings( Object.entries(item as any || {}).map(([protocol, binding]) => diff --git a/src/models/v2/contact.ts b/src/models/v2/contact.ts index c7ecb5122..dee4427ae 100644 --- a/src/models/v2/contact.ts +++ b/src/models/v2/contact.ts @@ -1,11 +1,13 @@ import { BaseModel } from "../base"; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; -import type { ContactInterface } from "../../models/contact"; +import type { ContactInterface } from "../contact"; +import type { ExtensionsInterface } from "../extensions"; -export class Contact extends Mixin(BaseModel, ExtensionsMixin) implements ContactInterface { +import type { v2 } from "../../spec-types"; + +export class Contact extends BaseModel implements ContactInterface { hasName(): boolean { return !!this._json.name; } @@ -29,4 +31,8 @@ export class Contact extends Mixin(BaseModel, ExtensionsMixin) implements Contac email(): string | undefined { return this._json.email; } + + extensions(): ExtensionsInterface { + return extensions(this); + } } \ No newline at end of file diff --git a/src/models/v2/correlation-id.ts b/src/models/v2/correlation-id.ts index 127504429..291bbd983 100644 --- a/src/models/v2/correlation-id.ts +++ b/src/models/v2/correlation-id.ts @@ -1,12 +1,21 @@ import { BaseModel } from "../base"; -import { Mixin } from '../utils'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; +import { hasDescription, description, extensions } from './mixins'; import type { CorrelationIdInterface } from "../correlation-id"; +import type { ExtensionsInterface } from "../extensions"; + +import type { v2 } from "../../spec-types"; + +export class CorrelationId extends BaseModel implements CorrelationIdInterface { + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } -export class CorrelationId extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements CorrelationIdInterface { hasLocation(): boolean { return !!this._json.location; } @@ -14,4 +23,8 @@ export class CorrelationId extends Mixin(BaseModel, DescriptionMixin, Extensions location(): string | undefined { return this._json.location; } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/extension.ts b/src/models/v2/extension.ts new file mode 100644 index 000000000..bb4588349 --- /dev/null +++ b/src/models/v2/extension.ts @@ -0,0 +1,19 @@ +import { BaseModel } from "../base"; + +import type { ExtensionInterface } from "../extension"; + +import type { v2 } from "../../spec-types"; + +export class Extension extends BaseModel implements ExtensionInterface { + name(): string { + return this._meta.name; + } + + version(): string { + return 'to implement'; + } + + value(): any { + return this._json; + } +} diff --git a/src/models/v2/extensions.ts b/src/models/v2/extensions.ts new file mode 100644 index 000000000..fb14599fe --- /dev/null +++ b/src/models/v2/extensions.ts @@ -0,0 +1,16 @@ +import { Collection } from '../collection'; + +import type { ExtensionsInterface } from "../extensions"; +import type { ExtensionInterface } from "../extension"; + +export class Extensions extends Collection implements ExtensionsInterface { + override get(name: string): ExtensionInterface | undefined { + name = name.startsWith('x-') ? name : `x-${name}`; + return this.collections.find(ext => ext.name() === name); + }; + + override has(name: string): boolean { + name = name.startsWith('x-') ? name : `x-${name}`; + return this.collections.some(ext => ext.name() === name); + }; +} diff --git a/src/models/v2/external-docs.ts b/src/models/v2/external-docs.ts new file mode 100644 index 000000000..f433f49f3 --- /dev/null +++ b/src/models/v2/external-docs.ts @@ -0,0 +1,26 @@ +import { BaseModel } from "../base"; + +import { hasDescription, description, extensions } from "./mixins"; + +import type { ExternalDocumentationInterface } from '../external-docs'; +import type { ExtensionsInterface } from "../extensions"; + +import type { v2 } from "../../spec-types"; + +export class ExternalDocumentation extends BaseModel implements ExternalDocumentationInterface { + url(): string { + return this._json.url; + } + + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } +} \ No newline at end of file diff --git a/src/models/v2/index.ts b/src/models/v2/index.ts index d56f9e6e6..69b94f651 100644 --- a/src/models/v2/index.ts +++ b/src/models/v2/index.ts @@ -1,16 +1,37 @@ export { AsyncAPIDocument as AsyncAPIDocumentV2 } from './asyncapi'; +export { Binding as BindingV2 } from './binding'; +export { Bindings as BindingsV2 } from './bindings'; +export { ChannelParameter as ChannelParameterV2 } from './channel-parameter'; +export { ChannelParameters as ChannelParametersV2 } from './channel-parameters'; +export { Channel as ChannelV2 } from './channel'; +export { Channels as ChannelsV2 } from './channels'; +export { Components as ComponentsV2 } from './components'; export { Contact as ContactV2 } from './contact'; +export { CorrelationId as CorrelationIdV2 } from './correlation-id'; +export { Extension as ExtensionV2 } from './extension'; +export { Extensions as ExtensionsV2 } from './extensions'; +export { ExternalDocumentation as ExternalDocumentationV2 } from './external-docs'; export { Info as InfoV2 } from './info'; export { License as LicenseV2 } from './license'; -export { Bindings as BindingsV2, Binding as BindingV2 } from './mixins/bindings'; -export { Extensions as ExtensionsV2, Extension as ExtensionV2 } from './mixins/extensions'; -export { ExternalDocumentation as ExternalDocumentationV2 } from './mixins/external-docs'; -export { Tags as TagsV2, Tag as TagV2 } from './mixins/tags'; -export { Server as ServerV2 } from './server'; -export { Servers as ServersV2 } from './servers'; +export { MessageExample as MessageExampleV2 } from './message-example'; +export { MessageExamples as MessageExamplesV2 } from './message-examples'; +export { MessageTrait as MessageTraitV2 } from './message-trait'; +export { MessageTraits as MessageTraitsV2 } from './message-traits'; +export { Message as MessageV2 } from './message'; +export { Messages as MessagesV2 } from './messages'; +export { OAuthFlow as OAuthFlowV2 } from './oauth-flow'; +export { OAuthFlows as OAuthFlowsV2 } from './oauth-flows'; +export { OperationTrait as OperationTraitV2 } from './operation-trait'; +export { OperationTraits as OperationTraitsV2 } from './operation-traits'; +export { Operation as OperationV2 } from './operation'; +export { Operations as OperationsV2 } from './operations'; +export { Schema as SchemaV2 } from './schema'; +export { Schemas as SchemasV2 } from './schemas'; export { SecurityScheme as SecuritySchemeV2 } from './security-scheme'; export { SecuritySchemes as SecuritySchemesV2 } from './security-schemes'; export { ServerVariable as ServerVariableV2 } from './server-variable'; export { ServerVariables as ServerVariablesV2 } from './server-variables'; -export { OAuthFlow as OAuthFlowV2 } from './oauth-flow'; -export { OAuthFlows as OAuthFlowsV2 } from './oauth-flows'; +export { Server as ServerV2 } from './server'; +export { Servers as ServersV2 } from './servers'; +export { Tag as TagV2 } from './tag'; +export { Tags as TagsV2 } from './tags'; \ No newline at end of file diff --git a/src/models/v2/info.ts b/src/models/v2/info.ts index f88c10ffd..4415d5e7c 100644 --- a/src/models/v2/info.ts +++ b/src/models/v2/info.ts @@ -1,20 +1,22 @@ import { BaseModel } from "../base"; import { Contact } from "./contact"; +import { ExternalDocumentation } from "./external-docs"; import { License } from "./license"; +import { Tags } from "./tags"; +import { Tag } from "./tag"; -import { Mixin } from '../utils'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; -import { ExternalDocumentation } from './mixins/external-docs'; -import { Tags, Tag } from './mixins/tags'; +import { hasDescription, description, extensions } from './mixins'; import type { ContactInterface } from "../contact"; import type { InfoInterface } from "../info"; +import type { ExtensionsInterface } from "../extensions"; import type { ExternalDocumentationInterface } from "../external-docs"; import type { LicenseInterface } from "../license"; import type { TagsInterface } from "../tags"; -export class Info extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements InfoInterface { +import type { v2 } from "../../spec-types"; + +export class Info extends BaseModel implements InfoInterface { title(): string { return this._json.title; } @@ -31,6 +33,14 @@ export class Info extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) im return this._meta.asyncapi.parsed.id; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + hasTermsOfService(): boolean { return !!this._json.termsOfService; } @@ -72,4 +82,8 @@ export class Info extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) im const tags = this._meta.asyncapi.parsed.tags || []; return new Tags(tags.map((tag: any, idx: number) => this.createModel(Tag, tag, { pointer: `/tags/${idx}` }))); } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/license.ts b/src/models/v2/license.ts index 69cb09e35..fe1b6a946 100644 --- a/src/models/v2/license.ts +++ b/src/models/v2/license.ts @@ -1,11 +1,13 @@ import { BaseModel } from "../base"; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; -import type { LicenseInterface } from "../../models/license"; +import type { ExtensionsInterface } from "../extensions"; +import type { LicenseInterface } from "../license"; -export class License extends Mixin(BaseModel, ExtensionsMixin) implements LicenseInterface { +import type { v2 } from "../../spec-types"; + +export class License extends BaseModel implements LicenseInterface { name(): string { return this._json.name; } @@ -17,4 +19,8 @@ export class License extends Mixin(BaseModel, ExtensionsMixin) implements Licens url(): string | undefined { return this._json.url; } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/message-example.ts b/src/models/v2/message-example.ts index d9fedb931..a24f69c8a 100644 --- a/src/models/v2/message-example.ts +++ b/src/models/v2/message-example.ts @@ -1,16 +1,18 @@ import { BaseModel } from "../base"; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; +import type { ExtensionsInterface } from "../extensions"; import type { MessageExampleInterface } from "../message-example"; -export class MessageExample extends Mixin(BaseModel, ExtensionsMixin) implements MessageExampleInterface { +import type { v2 } from "../../spec-types"; + +export class MessageExample extends BaseModel implements MessageExampleInterface { hasName(): boolean { return !!this._json.name; } - name(): string { + name(): string | undefined { return this._json.name; } @@ -18,7 +20,7 @@ export class MessageExample extends Mixin(BaseModel, ExtensionsMixin) implements return !!this._json.summary; } - summary(): string { + summary(): string | undefined { return this._json.summary; } @@ -37,4 +39,8 @@ export class MessageExample extends Mixin(BaseModel, ExtensionsMixin) implements payload(): Record | undefined { return this._json.payload; } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/message-trait.ts b/src/models/v2/message-trait.ts index d97eca2bc..2216f8802 100644 --- a/src/models/v2/message-trait.ts +++ b/src/models/v2/message-trait.ts @@ -5,28 +5,20 @@ import { MessageExample } from './message-example'; import { Schema } from './schema'; import { getDefaultSchemaFormat } from '../../schema-parser'; +import { bindings, hasDescription, description, extensions, hasExternalDocs, externalDocs, tags } from './mixins'; -import { Mixin } from '../utils'; -import { BindingsMixin } from './mixins/bindings'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; -import { ExternalDocumentationMixin } from './mixins/external-docs'; -import { TagsMixin } from './mixins/tags'; - -import type { ModelMetadata } from "../base"; +import type { BindingsInterface } from "../bindings"; import type { CorrelationIdInterface } from "../correlation-id"; +import type { ExtensionsInterface } from "../extensions"; +import type { ExternalDocumentationInterface } from "../external-docs"; import type { MessageExamplesInterface } from "../message-examples"; import type { MessageTraitInterface } from "../message-trait"; import type { SchemaInterface } from "../schema"; +import type { TagsInterface } from "../tags"; -export class MessageTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) implements MessageTraitInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string } = {} as any - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class MessageTrait extends BaseModel implements MessageTraitInterface { id(): string { return this.messageId() || this._meta.id; } @@ -93,6 +85,22 @@ export class MessageTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMix return this._json.summary; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + + hasExternalDocs(): boolean { + return hasExternalDocs(this); + } + + externalDocs(): ExternalDocumentationInterface | undefined { + return externalDocs(this); + } + examples(): MessageExamplesInterface { return new MessageExamples( (this._json.examples || []).map((example: any, index: number) => { @@ -100,4 +108,16 @@ export class MessageTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMix }) ); } + + tags(): TagsInterface { + return tags(this); + } + + bindings(): BindingsInterface { + return bindings(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/message.ts b/src/models/v2/message.ts index d1142e8f5..25e2617d7 100644 --- a/src/models/v2/message.ts +++ b/src/models/v2/message.ts @@ -18,7 +18,9 @@ import type { ServersInterface } from "../servers"; import type { ServerInterface } from "../server"; import type { SchemaInterface } from "../schema"; -export class Message extends MessageTrait implements MessageInterface { +import type { v2 } from "../../spec-types"; + +export class Message extends MessageTrait implements MessageInterface { hasPayload(): boolean { return !!this._json.payload; } diff --git a/src/models/v2/mixins.ts b/src/models/v2/mixins.ts new file mode 100644 index 000000000..10d083b4b --- /dev/null +++ b/src/models/v2/mixins.ts @@ -0,0 +1,66 @@ +import { Bindings } from "./bindings"; +import { Binding } from "./binding"; +import { Extensions } from "./extensions"; +import { Extension } from "./extension"; +import { ExternalDocumentation } from "./external-docs"; +import { Tags } from "./tags"; +import { Tag } from "./tag"; + +import { createModel } from "../utils"; +import { EXTENSION_REGEX } from '../../constants'; + +import type { BaseModel } from "../base"; +import type { BindingsInterface } from "../bindings"; +import type { ExtensionsInterface } from "../extensions"; +import type { ExtensionInterface } from "../extension"; +import type { ExternalDocumentationInterface } from "../external-docs"; +import type { TagsInterface } from "../tags"; + +import type { v2 } from "../../spec-types"; + +export function bindings(model: BaseModel<{ bindings?: Record }>): BindingsInterface { + return new Bindings( + Object.entries(model.json('bindings') || {}).map(([protocol, binding]) => + createModel(Binding, binding, { id: protocol, pointer: model.jsonPath(`bindings/${protocol}`) }, model) + ) + ); +} + +export function hasDescription(model: BaseModel<{ description?: string }>) { + return Boolean(model.json('description')); +}; + +export function description(model: BaseModel<{ description?: string }>): string | undefined { + return model.json('description'); +} + +export function extensions(model: BaseModel): ExtensionsInterface { + const extensions: ExtensionInterface[] = []; + Object.entries(model.json()).forEach(([key, value]) => { + if (EXTENSION_REGEX.test(key)) { + extensions.push( + createModel(Extension, value, { id: key, pointer: model.jsonPath(key) }, model) + ); + } + }); + return new Extensions(extensions); +}; + +export function hasExternalDocs(model: BaseModel<{ externalDocs?: v2.ExternalDocumentationObject }>): boolean { + return Object.keys(model.json('externalDocs') || {}).length > 0; +}; + +export function externalDocs(model: BaseModel): ExternalDocumentationInterface | undefined { + if (hasExternalDocs(model)) { + return new ExternalDocumentation(model.json('externalDocs')); + } + return; +}; + +export function tags(model: BaseModel<{ tags?: v2.TagsObject }>): TagsInterface { + return new Tags( + (model.json('tags') || []).map((tag: any, idx: number) => + createModel(Tag, tag, { pointer: model.jsonPath(`tags/${idx}`) }, model) + ) + ); +} diff --git a/src/models/v2/mixins/bindings.ts b/src/models/v2/mixins/bindings.ts deleted file mode 100644 index d15d8ddb1..000000000 --- a/src/models/v2/mixins/bindings.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { BaseModel } from "../../base"; -import { Collection } from '../../collection'; - -import { Mixin } from '../../utils'; -import { ExtensionsMixin } from './extensions'; - -import type { ModelMetadata } from "../../base"; -import type { BindingsMixinInterface } from "../../mixins"; -import type { BindingsInterface } from "../../bindings"; -import type { BindingInterface } from "../../binding"; - -export class Binding extends Mixin(BaseModel, ExtensionsMixin) implements BindingInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { protocol: string } = {} as any, - ) { - super(_json, _meta); - } - - protocol(): string { - return this._meta.protocol; - } - - version(): string { - return this._json.bindingVersion; - } - - value(): any { - const value = { ...this._json }; - delete value.bindingVersion; - return value; - } -} - -export class Bindings extends Collection implements BindingsInterface { - override get(name: string): BindingInterface | undefined { - return this.collections.find(binding => binding.protocol() === name); - }; - - override has(name: string): boolean { - return this.collections.some(binding => binding.protocol() === name); - }; -} - -export abstract class BindingsMixin extends BaseModel implements BindingsMixinInterface { - bindings(): BindingsInterface { - return new Bindings( - Object.entries(this._json.bindings || {}).map(([protocol, binding]) => - this.createModel(Binding, binding, { id: protocol, pointer: `${this._meta.pointer}/bindings/${protocol}` }) - ) - ); - } -} diff --git a/src/models/v2/mixins/description.ts b/src/models/v2/mixins/description.ts deleted file mode 100644 index c60f5c8e7..000000000 --- a/src/models/v2/mixins/description.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BaseModel } from "../../base"; - -import type { DescriptionMixinInterface } from "../../mixins"; - -export abstract class DescriptionMixin extends BaseModel implements DescriptionMixinInterface { - hasDescription() { - return Boolean(this._json.description); - }; - - description(): string | undefined { - return this._json.description; - } -} \ No newline at end of file diff --git a/src/models/v2/mixins/extensions.ts b/src/models/v2/mixins/extensions.ts deleted file mode 100644 index cfee5c5af..000000000 --- a/src/models/v2/mixins/extensions.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Collection } from '../../collection'; -import { BaseModel } from "../../base"; - -import type { ModelMetadata } from "../../base"; -import type { ExtensionsMixinInterface } from "../../mixins"; -import type { ExtensionsInterface } from "../../extensions"; -import type { ExtensionInterface } from "../../extension"; - -import { EXTENSION_REGEX } from '../../../constants'; - -export class Extension extends BaseModel implements ExtensionInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { name: string } = {} as any, - ) { - super(_json, _meta); - } - - name(): string { - return this._meta.name; - } - - version(): string { - return 'to implement'; - } - - value(): any { - return this._json; - } -} - -export class Extensions extends Collection implements ExtensionsInterface { - override get(name: string): ExtensionInterface | undefined { - name = name.startsWith('x-') ? name : `x-${name}`; - return this.collections.find(ext => ext.name() === name); - }; - - override has(name: string): boolean { - name = name.startsWith('x-') ? name : `x-${name}`; - return this.collections.some(ext => ext.name() === name); - }; -} - -export abstract class ExtensionsMixin extends BaseModel implements ExtensionsMixinInterface { - extensions(): ExtensionsInterface { - const extensions: Extension[] = []; - Object.entries(this._json).forEach(([key, value]) => { - if (EXTENSION_REGEX.test(key)) { - extensions.push( - this.createModel(Extension, value, { id: key, pointer: `${this._meta.pointer}/${key}` }) - ); - } - }); - return new Extensions(extensions); - }; -} \ No newline at end of file diff --git a/src/models/v2/mixins/external-docs.ts b/src/models/v2/mixins/external-docs.ts deleted file mode 100644 index 92906fb0e..000000000 --- a/src/models/v2/mixins/external-docs.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { BaseModel } from "../../base"; - -import { Mixin } from '../../utils'; -import { DescriptionMixin } from './description'; -import { ExtensionsMixin } from './extensions'; - -import type { ExternalDocumentationInterface } from '../../external-docs'; -import type { ExternalDocumentationMixinInterface } from "../../mixins"; - -export class ExternalDocumentation extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements ExternalDocumentationInterface { - url(): string { - return this._json.url; - } -} - -export abstract class ExternalDocumentationMixin extends BaseModel implements ExternalDocumentationMixinInterface { - hasExternalDocs(): boolean { - return Object.keys(this._json.externalDocs || {}).length > 0; - }; - - externalDocs(): ExternalDocumentationInterface | undefined { - if (this.hasExternalDocs()) { - return new ExternalDocumentation(this._json.externalDocs); - } - return; - }; -} \ No newline at end of file diff --git a/src/models/v2/mixins/tags.ts b/src/models/v2/mixins/tags.ts deleted file mode 100644 index eaf49931b..000000000 --- a/src/models/v2/mixins/tags.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BaseModel } from "../../base"; -import { Collection } from "../../collection"; - -import { Mixin } from '../../utils'; -import { DescriptionMixin } from './description'; -import { ExtensionsMixin } from './extensions'; -import { ExternalDocumentationMixin } from './external-docs'; - -import type { TagsMixinInterface } from "../../mixins"; -import type { TagsInterface } from "../../tags"; -import type { TagInterface } from "../../tag"; - -export class Tag extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin) implements TagInterface { - name(): string { - return this._json.name; - } -} - -export class Tags extends Collection implements TagsInterface { - override get(name: string): TagInterface | undefined { - return this.collections.find(tag => tag.name() === name); - }; - - override has(name: string): boolean { - return this.collections.some(tag => tag.name() === name); - }; -} - -export abstract class TagsMixin extends BaseModel implements TagsMixinInterface { - tags(): TagsInterface { - return new Tags( - (this._json.tags || []).map((tag: any, idx: number) => - this.createModel(Tag, tag, { pointer: `${this._meta.pointer}/tags/${idx}` }) - ) - ); - } -} \ No newline at end of file diff --git a/src/models/v2/oauth-flow.ts b/src/models/v2/oauth-flow.ts index daeade1cb..e994eb4f1 100644 --- a/src/models/v2/oauth-flow.ts +++ b/src/models/v2/oauth-flow.ts @@ -1,13 +1,15 @@ import { BaseModel } from '../base'; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; +import type { ExtensionsInterface } from '../extensions'; import type { OAuthFlowInterface } from '../oauth-flow'; -export class OAuthFlow extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowInterface { +import type { v2 } from "../../spec-types"; + +export class OAuthFlow extends BaseModel implements OAuthFlowInterface { authorizationUrl(): string | undefined { - return this._json.authorizationUrl; + return this.json().authorizationUrl; } hasRefreshUrl(): boolean { @@ -23,6 +25,10 @@ export class OAuthFlow extends Mixin(BaseModel, ExtensionsMixin) implements OAut } tokenUrl(): string | undefined { - return this._json.tokenUrl; + return this.json>().tokenUrl; + } + + extensions(): ExtensionsInterface { + return extensions(this); } } \ No newline at end of file diff --git a/src/models/v2/oauth-flows.ts b/src/models/v2/oauth-flows.ts index d83f291ed..f8f579d33 100644 --- a/src/models/v2/oauth-flows.ts +++ b/src/models/v2/oauth-flows.ts @@ -1,18 +1,20 @@ import { BaseModel } from '../base'; - import { OAuthFlow } from './oauth-flow'; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; +import { extensions } from './mixins'; + +import type { ExtensionsInterface } from '../extensions'; import type { OAuthFlowsInterface } from '../oauth-flows'; import type { OAuthFlowInterface } from '../oauth-flow'; -export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowsInterface { +import type { v2 } from "../../spec-types"; + +export class OAuthFlows extends BaseModel implements OAuthFlowsInterface { hasAuthorizationCode(): boolean { return !!this._json.authorizationCode; } - authorizationCode(): OAuthFlowInterface | undefined { + authorizationCode(): OAuthFlowInterface | undefined { if (!this._json.authorizationCode) return undefined; return new OAuthFlow(this._json.authorizationCode); } @@ -21,7 +23,7 @@ export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAu return !!this._json.clientCredentials; } - clientCredentials(): OAuthFlowInterface | undefined { + clientCredentials(): OAuthFlowInterface | undefined { if (!this._json.clientCredentials) return undefined; return new OAuthFlow(this._json.clientCredentials); } @@ -30,7 +32,7 @@ export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAu return !!this._json.implicit; } - implicit(): OAuthFlowInterface | undefined { + implicit(): OAuthFlowInterface | undefined { if (!this._json.implicit) return undefined; return new OAuthFlow(this._json.implicit); } @@ -39,8 +41,12 @@ export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAu return !!this._json.password; } - password(): OAuthFlowInterface | undefined { + password(): OAuthFlowInterface | undefined { if (!this._json.password) return undefined; return new OAuthFlow(this._json.password); } + + extensions(): ExtensionsInterface { + return extensions(this); + } } \ No newline at end of file diff --git a/src/models/v2/operation-trait.ts b/src/models/v2/operation-trait.ts index 9f9960558..6a24b2e5d 100644 --- a/src/models/v2/operation-trait.ts +++ b/src/models/v2/operation-trait.ts @@ -1,26 +1,19 @@ import { BaseModel } from "../base"; import { SecurityScheme } from './security-scheme'; -import { Mixin } from '../utils'; -import { BindingsMixin } from './mixins/bindings'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; -import { ExternalDocumentationMixin } from './mixins/external-docs'; -import { TagsMixin } from './mixins/tags'; - -import type { ModelMetadata } from "../base"; +import { bindings, hasDescription, description, extensions, hasExternalDocs, externalDocs, tags } from './mixins'; + +import type { BindingsInterface } from "../bindings"; +import type { ExtensionsInterface } from "../extensions"; +import type { ExternalDocumentationInterface } from "../external-docs"; import type { OperationAction } from "../operation"; import type { OperationTraitInterface } from "../operation-trait"; import type { SecuritySchemeInterface } from "../security-scheme"; +import type { TagsInterface } from "../tags"; -export class OperationTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) implements OperationTraitInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string, action: OperationAction } = {} as any, - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class OperationTrait extends BaseModel implements OperationTraitInterface { id(): string { return this.operationId() || this._meta.id; } @@ -45,8 +38,24 @@ export class OperationTrait extends Mixin(BaseModel, BindingsMixin, DescriptionM return this._json.summary; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + + hasExternalDocs(): boolean { + return hasExternalDocs(this); + } + + externalDocs(): ExternalDocumentationInterface | undefined { + return externalDocs(this); + } + security(): Array> { - const securitySchemes = this._meta?.asyncapi?.parsed.components.securitySchemes || {}; + const securitySchemes = this._meta.asyncapi?.parsed?.components?.securitySchemes || {}; return (this._json.security || []).map((requirement: any) => { const requirements: Record = {}; Object.entries(requirement).forEach(([security, scopes]) => { @@ -58,4 +67,16 @@ export class OperationTrait extends Mixin(BaseModel, BindingsMixin, DescriptionM return requirements; }) } + + tags(): TagsInterface { + return tags(this); + } + + bindings(): BindingsInterface { + return bindings(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/operation.ts b/src/models/v2/operation.ts index 1aff51205..f0a9c43d5 100644 --- a/src/models/v2/operation.ts +++ b/src/models/v2/operation.ts @@ -16,7 +16,9 @@ import type { OperationTraitsInterface } from "../operation-traits"; import type { ServersInterface } from "../servers"; import type { ServerInterface } from "../server"; -export class Operation extends OperationTrait implements OperationInterface { +import type { v2 } from "../../spec-types"; + +export class Operation extends OperationTrait implements OperationInterface { servers(): ServersInterface { const servers: ServerInterface[] = []; const serversData: any[] = []; @@ -45,12 +47,14 @@ export class Operation extends OperationTrait implements OperationInterface { messages(): MessagesInterface { let isOneOf = false; - let messages = this._json.message || []; - if (Array.isArray(messages.oneOf)) { - messages = messages.oneOf; - isOneOf = true; - } else if (!Array.isArray(messages)) { - messages = [messages]; + let messages: Array = []; + if (this._json.message) { + if (Array.isArray((this._json.message as { oneOf?: Array }).oneOf)) { + messages = (this._json.message as unknown as { oneOf: Array }).oneOf; + isOneOf = true; + } else { + messages = [this._json.message as unknown as v2.MessageObject]; + } } return new Messages( diff --git a/src/models/v2/schema.ts b/src/models/v2/schema.ts index 0029a3658..258ae0ebc 100644 --- a/src/models/v2/schema.ts +++ b/src/models/v2/schema.ts @@ -1,20 +1,14 @@ import { BaseModel } from "../base"; -import { Mixin } from '../utils'; -import { ExtensionsMixin } from './mixins/extensions'; -import { ExternalDocumentationMixin } from './mixins/external-docs'; +import { extensions, hasExternalDocs, externalDocs } from './mixins'; -import type { ModelMetadata } from "../base"; +import type { ExtensionsInterface } from "../extensions"; +import type { ExternalDocumentationInterface } from "../external-docs"; import type { SchemaInterface } from "../schema"; -export class Schema extends Mixin(BaseModel, ExtensionsMixin, ExternalDocumentationMixin) implements SchemaInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string, parent: Schema | null } = {} as any - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class Schema extends BaseModel implements SchemaInterface { uid(): string { return this._meta.id; } @@ -257,4 +251,16 @@ export class Schema extends Mixin(BaseModel, ExtensionsMixin, ExternalDocumentat writeOnly(): boolean | undefined { return this._json.writeOnly || false; } + + hasExternalDocs(): boolean { + return hasExternalDocs(this); + } + + externalDocs(): ExternalDocumentationInterface | undefined { + return externalDocs(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/security-scheme.ts b/src/models/v2/security-scheme.ts index cf00444f9..2cd6c46c1 100644 --- a/src/models/v2/security-scheme.ts +++ b/src/models/v2/security-scheme.ts @@ -1,26 +1,27 @@ import { BaseModel } from '../base'; - import { OAuthFlows } from './oauth-flows'; -import { Mixin } from '../utils'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; -import type { ModelMetadata } from '../base'; +import { hasDescription, description, extensions } from './mixins'; + +import type { ExtensionsInterface } from '../extensions'; import type { SecuritySchemaType, SecuritySchemeInterface } from '../security-scheme'; import type { OAuthFlowsInterface } from '../oauth-flows'; -export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements SecuritySchemeInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string } = {} as any - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class SecurityScheme extends BaseModel implements SecuritySchemeInterface { id(): string { return this._meta.id; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + hasBearerFormat(): boolean { return !!this._json.bearerFormat; } @@ -29,7 +30,7 @@ export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, Extension return this._json.bearerFormat; } - openIdConnectUrl(): string { + openIdConnectUrl(): string | undefined { return this._json.openIdConnectUrl; } @@ -42,10 +43,6 @@ export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, Extension return new OAuthFlows(this._json.flows); } - scopes(): string[] { - return this._json.scopes; - } - type(): SecuritySchemaType { return this._json.type; } @@ -53,4 +50,8 @@ export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, Extension in(): string | undefined { return this._json.in; } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/server-variable.ts b/src/models/v2/server-variable.ts index 99134ce91..e4ecdd356 100644 --- a/src/models/v2/server-variable.ts +++ b/src/models/v2/server-variable.ts @@ -1,24 +1,25 @@ import { BaseModel } from '../base'; -import { Mixin } from '../utils'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; +import { hasDescription, description, extensions } from './mixins'; -import type { ModelMetadata } from '../base'; +import type { ExtensionsInterface } from '../extensions'; import type { ServerVariableInterface } from '../server-variable'; -export class ServerVariable extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements ServerVariableInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string } = {} as any - ) { - super(_json, _meta); - } - +import type { v2 } from "../../spec-types"; + +export class ServerVariable extends BaseModel implements ServerVariableInterface { id(): string { return this._meta.id; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + hasDefaultValue(): boolean { return !!this._json.default } @@ -32,10 +33,14 @@ export class ServerVariable extends Mixin(BaseModel, DescriptionMixin, Extension } allowedValues(): Array { - return this._json.enum; + return this._json.enum || []; } examples(): Array { - return this._json.examples + return this._json.examples || []; + } + + extensions(): ExtensionsInterface { + return extensions(this); } } diff --git a/src/models/v2/server.ts b/src/models/v2/server.ts index a6ad0fad8..821f7c831 100644 --- a/src/models/v2/server.ts +++ b/src/models/v2/server.ts @@ -7,14 +7,9 @@ import { SecurityScheme } from './security-scheme'; import { ServerVariables } from './server-variables'; import { ServerVariable } from './server-variable'; -import { Mixin } from '../utils'; -import { BindingsMixin } from './mixins/bindings'; -import { DescriptionMixin } from './mixins/description'; -import { ExtensionsMixin } from './mixins/extensions'; - +import { bindings, hasDescription, description, extensions } from './mixins'; import { tilde } from "../../utils"; -import type { ModelMetadata } from "../base"; import type { ChannelsInterface } from '../channels'; import type { ChannelInterface } from '../channel'; import type { OperationsInterface } from '../operations'; @@ -24,15 +19,12 @@ import type { MessageInterface } from '../message'; import type { ServerInterface } from '../server'; import type { ServerVariablesInterface } from '../server-variables'; import type { SecuritySchemeInterface } from '../security-scheme'; +import type { ExtensionsInterface } from '../extensions'; +import type { BindingsInterface } from '../bindings'; -export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin) implements ServerInterface { - constructor( - _json: Record, - protected readonly _meta: ModelMetadata & { id: string } = {} as any, - ) { - super(_json, _meta); - } +import type { v2 } from "../../spec-types"; +export class Server extends BaseModel implements ServerInterface { id(): string { return this._meta.id; } @@ -49,10 +41,18 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex return !!this._json.protocolVersion; } - protocolVersion(): string { + protocolVersion(): string | undefined { return this._json.protocolVersion; } + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + channels(): ChannelsInterface { const channels: ChannelInterface[] = []; Object.entries(this._meta.asyncapi?.parsed.channels || {}).map(([channelAddress, channel]: [string, any]) => { @@ -90,7 +90,7 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex } security(): Array> { - const securitySchemes = this._meta?.asyncapi?.parsed.components.securitySchemes || {}; + const securitySchemes = this._meta?.asyncapi?.parsed?.components?.securitySchemes || {}; return (this._json.security || []).map((requirement: any) => { const requirements: Record = {}; Object.entries(requirement).forEach(([security, scopes]) => { @@ -102,4 +102,12 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex return requirements; }) } + + bindings(): BindingsInterface { + return bindings(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } } diff --git a/src/models/v2/tag.ts b/src/models/v2/tag.ts new file mode 100644 index 000000000..2a188cc7c --- /dev/null +++ b/src/models/v2/tag.ts @@ -0,0 +1,35 @@ +import { BaseModel } from "../base"; + +import { hasDescription, description, extensions, hasExternalDocs, externalDocs } from "./mixins"; + +import type { ExtensionsInterface } from "../extensions"; +import type{ ExternalDocumentationInterface } from "../external-docs"; +import type { TagInterface } from "../tag"; + +import type { v2 } from "../../spec-types"; + +export class Tag extends BaseModel implements TagInterface { + name(): string { + return this._json.name; + } + + hasDescription(): boolean { + return hasDescription(this); + } + + description(): string | undefined { + return description(this); + } + + extensions(): ExtensionsInterface { + return extensions(this); + } + + hasExternalDocs(): boolean { + return hasExternalDocs(this); + } + + externalDocs(): ExternalDocumentationInterface | undefined { + return externalDocs(this); + } +} diff --git a/src/models/v2/tags.ts b/src/models/v2/tags.ts new file mode 100644 index 000000000..133845b64 --- /dev/null +++ b/src/models/v2/tags.ts @@ -0,0 +1,14 @@ +import { Collection } from "../collection"; + +import type { TagsInterface } from "../tags"; +import type { TagInterface } from "../tag"; + +export class Tags extends Collection implements TagsInterface { + override get(name: string): TagInterface | undefined { + return this.collections.find(tag => tag.name() === name); + }; + + override has(name: string): boolean { + return this.collections.some(tag => tag.name() === name); + }; +} diff --git a/src/schema-parser/spectral-rule-v2.ts b/src/schema-parser/spectral-rule-v2.ts index 34af4d429..b4048edf0 100644 --- a/src/schema-parser/spectral-rule-v2.ts +++ b/src/schema-parser/spectral-rule-v2.ts @@ -8,6 +8,7 @@ import type { RuleDefinition } from "@stoplight/spectral-core"; import type { Parser } from '../parser'; import type { ValidateSchemaInput } from './index'; import type { SchemaValidateResult } from '../types'; +import type { v2 } from '../spec-types'; export function aas2schemaParserRule(parser: Parser): RuleDefinition { return { @@ -52,7 +53,7 @@ function rulesetFunction(parser: Parser) { } const path = [...ctx.path, 'payload']; - const spec = ctx.document.data as { asyncapi: string }; + const spec = ctx.document.data as v2.AsyncAPIObject; const schemaFormat = getSchemaFormat(targetVal.schemaFormat, spec.asyncapi); const defaultSchemaFormat = getDefaultSchemaFormat(spec.asyncapi); // we don't have a parsed specification yet because we are still executing code in the context of spectral diff --git a/src/spec-types/index.ts b/src/spec-types/index.ts new file mode 100644 index 000000000..1c818d3fa --- /dev/null +++ b/src/spec-types/index.ts @@ -0,0 +1 @@ +export * as v2 from './v2'; diff --git a/src/spec-types/v2.ts b/src/spec-types/v2.ts new file mode 100644 index 000000000..a5dfbdeb2 --- /dev/null +++ b/src/spec-types/v2.ts @@ -0,0 +1,380 @@ +import type { JSONSchema7, JSONSchema7Type } from "json-schema"; + +export type AsyncAPIVersion = string; +export type Identifier = string; +export type DefaultContentType = string; + +export interface AsyncAPIObject extends SpecificationExtensions { + asyncapi: AsyncAPIVersion; + id?: Identifier; + info: InfoObject; + defaultContentType?: DefaultContentType; + servers?: ServersObject; + channels: ChannelsObject; + components?: ComponentsObject; + tags?: TagsObject; + externalDocs?: ExternalDocumentationObject; +} + +export interface InfoObject extends SpecificationExtensions { + title: string; + version: string; + description?: string; + termsOfService?: string; + contact?: ContactObject; + license?: LicenseObject; +} + +export interface ContactObject extends SpecificationExtensions { + name?: string; + url?: string; + email?: string; +} + +export interface LicenseObject extends SpecificationExtensions { + name: string; + url?: string; +} + +export type ServersObject = Record; + +export interface ServerObject extends SpecificationExtensions { + url: string; + protocol: string; + protocolVersion?: string; + description?: string; + variables?: Record; + security?: Array; + bindings?: ServerBindingsObject | ReferenceObject; +} + +export interface ServerVariableObject extends SpecificationExtensions { + enum?: Array; + default?: string; + description?: string; + examples?: Array; +} + +export interface ServerBindingsObject extends SpecificationExtensions { + http: Binding; + ws: Binding; + kafka: Binding; + anypointmq: Binding; + amqp: Binding; + amqp1: Binding; + mqtt: Binding; + mqtt5: Binding; + nats: Binding; + jms: Binding; + sns: Binding; + sqs: Binding; + stomp: Binding; + redis: Binding; + mercure: Binding; + ibmmq: Binding; +} + +export type ChannelsObject = Record; + +export interface ChannelObject extends SpecificationExtensions { + description?: string; + servers?: Array; + subscribe?: OperationObject; + publish?: OperationObject; + parameters?: ParametersObject; + bindings?: ChannelBindingsObject | ReferenceObject; +} + +export interface ChannelBindingsObject extends SpecificationExtensions { + http: Binding; + ws: Binding; + kafka: Binding; + anypointmq: Binding; + amqp: Binding; + amqp1: Binding; + mqtt: Binding; + mqtt5: Binding; + nats: Binding; + jms: Binding; + sns: Binding; + sqs: Binding; + stomp: Binding; + redis: Binding; + mercure: Binding; + ibmmq: Binding; +} + +export interface OperationObject extends OperationTraitObject, SpecificationExtensions { + message?: MessageObject | ReferenceObject | { oneOf: Array }; + traits?: Array; +} + +export interface OperationTraitObject extends SpecificationExtensions { + operationId?: string; + summary?: string; + description?: string; + security?: Array; + tags?: TagsObject; + externalDocs?: ExternalDocumentationObject; + bindings?: OperationBindingsObject | ReferenceObject; +} + +export interface OperationBindingsObject extends SpecificationExtensions { + http: Binding; + ws: Binding; + kafka: Binding; + anypointmq: Binding; + amqp: Binding; + amqp1: Binding; + mqtt: Binding; + mqtt5: Binding; + nats: Binding; + jms: Binding; + sns: Binding; + sqs: Binding; + stomp: Binding; + redis: Binding; + mercure: Binding; + ibmmq: Binding; +} + +export type ParametersObject = Record; + +export interface ParameterObject extends SpecificationExtensions { + description?: string; + schema?: SchemaObject; + location?: string; +} + +export interface MessageObject extends MessageTraitObject, SpecificationExtensions { + payload?: any; + traits?: Array; +} + +export interface MessageTraitObject extends SpecificationExtensions { + messageId?: string; + headers?: SchemaObject; + correlationId?: CorrelationIDObject | ReferenceObject; + schemaFormat?: string; + contentType?: string; + name?: string; + title?: string; + summary?: string; + description?: string; + tags?: TagsObject; + externalDocs?: ExternalDocumentationObject; + bindings?: MessageBindingsObject | ReferenceObject; + examples?: Array; +} + +export interface MessageExampleObject extends SpecificationExtensions { + name?: string; + summary?: string; + headers?: Record; + payload?: any; +} + +export interface MessageBindingsObject extends SpecificationExtensions { + http: Binding; + ws: Binding; + kafka: Binding; + anypointmq: Binding; + amqp: Binding; + amqp1: Binding; + mqtt: Binding; + mqtt5: Binding; + nats: Binding; + jms: Binding; + sns: Binding; + sqs: Binding; + stomp: Binding; + redis: Binding; + mercure: Binding; + ibmmq: Binding; +} + +export type TagsObject = Array; + +export interface TagObject extends SpecificationExtensions { + name: string; + description?: string; + externalDocs?: ExternalDocumentationObject; +} + +export interface ExternalDocumentationObject extends SpecificationExtensions { + url: string; + description?: string; +} + +export interface ComponentsObject extends SpecificationExtensions { + channels?: Record; + servers?: Record; + schemas?: Record; + messages?: Record; + securitySchemes?: Record; + parameters?: Record; + serverVariables?: Record; + correlationIds?: Record; + operationTraits?: Record; + messageTraits?: Record; + serverBindings?: Record; + channelBindings?: Record; + operationBindings?: Record; + messageBindings?: Record; +} + +export type SchemaObject = AsyncAPISchemaObject | ReferenceObject; + +export interface AsyncAPISchemaObject extends JSONSchema7, SpecificationExtensions { + discriminator?: string; + externalDocs?: ExternalDocumentationObject; + deprecated?: boolean; + examples?: Array | undefined; + [keyword: string]: any; +} + +export interface SecuritySchemeObject extends SpecificationExtensions { + type: SecuritySchemeType; + description?: string; + name?: string; + in?: 'user' | 'password' | 'query' | 'header' | 'cookie'; + scheme?: string; + bearerFormat?: string; + flows?: OAuthFlowsObject; + openIdConnectUrl?: string; +} + +export type SecuritySchemeType = + | 'userPassword' + | 'apiKey' + | 'X509' + | 'symmetricEncryption' + | 'asymmetricEncryption' + | 'httpApiKey' + | 'http' + | 'oauth2' + | 'openIdConnect' + | 'plain' + | 'scramSha256' + | 'scramSha512' + | 'gssapi'; + +export interface SecuritySchemeObjectBase extends SpecificationExtensions { + description?: string; +} + +export interface SecuritySchemeObjectUserPassword extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'userPassword'; +} + +export interface SecuritySchemeObjectApiKey extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'apiKey'; + in: 'user' | 'password'; +} + +export interface SecuritySchemeObjectX509 extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'X509'; +} + +export interface SecuritySchemeObjectSymetricEncryption extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'symmetricEncryption'; +} + +export interface SecuritySchemeObjectAsymetricEncryption extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'asymmetricEncryption'; +} + +export interface SecuritySchemeObjectHttpApiKey extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'httpApiKey'; + name: string; + in: 'query' | 'header' | 'cookie'; +} + +export interface SecuritySchemeObjectHttp extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'http'; + scheme: string; + bearerFormat?: string; +} + +export interface SecuritySchemeObjectOauth2 extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'oauth2'; + flows: OAuthFlowsObject; +} + +export interface SecuritySchemeObjectOpenIdConnect extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'openIdConnect'; + openIdConnectUrl: string; +} + +export interface SecuritySchemeObjectPlain extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'plain'; +} + +export interface SecuritySchemeObjectScramSha256 extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'scramSha256'; +} + +export interface SecuritySchemeObjectScramSha512 extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'scramSha512'; +} + +export interface SecuritySchemeObjectGssapi extends SecuritySchemeObjectBase, SpecificationExtensions { + type: 'gssapi'; +} + +export interface OAuthFlowsObject extends SpecificationExtensions { + implicit?: OAuthFlowObjectImplicit; + password?: OAuthFlowObjectPassword; + clientCredentials?: OAuthFlowObjectClientCredentials; + authorizationCode?: OAuthFlowObjectAuthorizationCode; +} + +export type OAuthFlowObject = + & OAuthFlowObjectImplicit + & OAuthFlowObjectPassword + & OAuthFlowObjectClientCredentials + & OAuthFlowObjectAuthorizationCode; + +export interface OAuthFlowObjectBase extends SpecificationExtensions { + refreshUrl?: string; + scopes: Record; +} + +export interface OAuthFlowObjectImplicit extends OAuthFlowObjectBase, SpecificationExtensions { + authorizationUrl: string; +} + +export interface OAuthFlowObjectPassword extends OAuthFlowObjectBase, SpecificationExtensions { + tokenUrl: string; +} + +export interface OAuthFlowObjectClientCredentials extends OAuthFlowObjectBase, SpecificationExtensions { + tokenUrl: string; +} + +export interface OAuthFlowObjectAuthorizationCode extends OAuthFlowObjectBase, SpecificationExtensions { + authorizationUrl: string; + tokenUrl: string; +} + +export type SecurityRequirementObject = Record>; + +export interface CorrelationIDObject extends SpecificationExtensions { + location: string; + description?: string; +} + +export interface Binding { + bindingVersion?: string; + [key: string]: any; +} + +export interface SpecificationExtensions { + [extension: `x-${string}`]: SpecificationExtension; +} + +export type SpecificationExtension = any; + +export interface ReferenceObject { + '$ref': string; +} \ No newline at end of file diff --git a/src/stringify.ts b/src/stringify.ts index 32240adee..802c95890 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -3,6 +3,8 @@ import { AsyncAPIDocumentInterface, newAsyncAPIDocument } from './models'; import { createDetailedAsyncAPI, isAsyncAPIDocument, isParsedDocument, isStringifiedDocument } from './utils'; import { xParserSpecStringified } from './constants'; +import type { DetailedAsyncAPI } from './types'; + export interface StringifyOptions { space?: string | number; } @@ -45,7 +47,7 @@ export function unstringify(document: unknown): AsyncAPIDocumentInterface | unde delete (>parsed)[String(xParserSpecStringified)]; traverseStringifiedDoc(document, undefined, document, new Map(), new Map()); - return newAsyncAPIDocument(createDetailedAsyncAPI(document as string, parsed as Record)); + return newAsyncAPIDocument(createDetailedAsyncAPI(document as string, parsed as DetailedAsyncAPI['parsed'] )); } function refReplacer() { diff --git a/src/types.ts b/src/types.ts index 9a292ff7b..b01afda1e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import type { ISpectralDiagnostic, IFunctionResult } from '@stoplight/spectral-core'; -import type { JSONSchema7 } from "json-schema" +import type { v2 } from "./spec-types"; export type MaybeAsyncAPI = { asyncapi: string } & Record; export interface AsyncAPISemver { @@ -12,10 +12,11 @@ export interface AsyncAPISemver { export interface DetailedAsyncAPI { source: string | Record; - parsed: Record; + parsed: AsyncAPIObject; semver: AsyncAPISemver; } export type Diagnostic = ISpectralDiagnostic; export type SchemaValidateResult = IFunctionResult; -export type AsyncAPISchema = JSONSchema7 & { [key: string]: any }; +export type AsyncAPIObject = v2.AsyncAPIObject; +export type AsyncAPISchema = v2.AsyncAPISchemaObject; diff --git a/src/utils.ts b/src/utils.ts index 48c0cd5f1..6679eb994 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,7 +8,7 @@ import { } from './constants'; import type { ISpectralDiagnostic } from '@stoplight/spectral-core'; -import type { AsyncAPISemver, DetailedAsyncAPI, MaybeAsyncAPI } from 'types'; +import type { AsyncAPISemver, AsyncAPIObject, DetailedAsyncAPI, MaybeAsyncAPI } from 'types'; export function toAsyncAPIDocument(maybeDoc: unknown): AsyncAPIDocumentInterface | undefined { if (isAsyncAPIDocument(maybeDoc)) { @@ -17,7 +17,7 @@ export function toAsyncAPIDocument(maybeDoc: unknown): AsyncAPIDocumentInterface if (!isParsedDocument(maybeDoc)) { return; } - return unstringify(maybeDoc) || newAsyncAPIDocument(createDetailedAsyncAPI(maybeDoc, maybeDoc)); + return unstringify(maybeDoc) || newAsyncAPIDocument(createDetailedAsyncAPI(maybeDoc, maybeDoc as any)); } export function isAsyncAPIDocument(maybeDoc: unknown): maybeDoc is AsyncAPIDocumentInterface { @@ -41,11 +41,11 @@ export function isStringifiedDocument(maybeDoc: unknown): maybeDoc is Record, parsed: Record): DetailedAsyncAPI { +export function createDetailedAsyncAPI(source: string | Record, parsed: AsyncAPIObject): DetailedAsyncAPI { return { source, parsed, - semver: getSemver((parsed as MaybeAsyncAPI).asyncapi), + semver: getSemver(parsed.asyncapi), } } diff --git a/test/custom-operations/parse-schema.spec.ts b/test/custom-operations/parse-schema.spec.ts index 08ba25a8a..941d2ab8d 100644 --- a/test/custom-operations/parse-schema.spec.ts +++ b/test/custom-operations/parse-schema.spec.ts @@ -3,6 +3,8 @@ import { Parser } from '../../src/parser'; import { parse } from '../../src/parse'; import { xParserOriginalPayload } from '../../src/constants'; +import type { v2 } from '../../src/spec-types'; + describe('custom operations - parse schemas', function() { const parser = new Parser(); @@ -32,9 +34,9 @@ describe('custom operations - parse schemas', function() { expect(parsed).toBeInstanceOf(AsyncAPIDocumentV2); expect(diagnostics.length > 0).toEqual(true); - expect(parsed?.json()['channels']['channel']['publish']['message']['payload']).toEqual({ type: 'object' }); - expect(parsed?.json()['channels']['channel']['publish']['message'][xParserOriginalPayload]).toEqual({ type: 'object' }); - expect(parsed?.json()['channels']['channel']['publish']['message']['payload']).toEqual(parsed?.json()['channels']['channel']['publish']['message'][xParserOriginalPayload]); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual({ type: 'object' }); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.[xParserOriginalPayload]).toEqual({ type: 'object' }); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.[xParserOriginalPayload]); }); it('should parse valid default schema format', async function() { @@ -62,9 +64,9 @@ describe('custom operations - parse schemas', function() { expect(parsed).toBeInstanceOf(AsyncAPIDocumentV2); expect(diagnostics.length > 0).toEqual(true); - expect(parsed?.json()['channels']['channel']['publish']['message']['payload']).toEqual({ type: 'object' }); - expect(parsed?.json()['channels']['channel']['publish']['message'][xParserOriginalPayload]).toEqual({ type: 'object' }); - expect(parsed?.json()['channels']['channel']['publish']['message']['payload']).toEqual(parsed?.json()['channels']['channel']['publish']['message'][xParserOriginalPayload]); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual({ type: 'object' }); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.[xParserOriginalPayload]).toEqual({ type: 'object' }); + expect((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.payload).toEqual((parsed?.json()?.channels?.channel?.publish?.message as v2.MessageObject)?.[xParserOriginalPayload]); }); it('should parse invalid schema format', async function() { diff --git a/test/models/asyncapi.spec.ts b/test/models/asyncapi.spec.ts index 1a7b410e4..de92886b3 100644 --- a/test/models/asyncapi.spec.ts +++ b/test/models/asyncapi.spec.ts @@ -5,7 +5,7 @@ import { createDetailedAsyncAPI } from '../../src/utils'; describe('AsyncAPIDocument factory', function() { it('should create a valid document from v2.0.0', function() { const doc = { asyncapi: "2.0.0" }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); const d = newAsyncAPIDocument(detailed) expect(d.version()).toEqual(doc.asyncapi); expect(d).toBeInstanceOf(AsyncAPIDocumentV2); @@ -13,7 +13,7 @@ describe('AsyncAPIDocument factory', function() { it('should fail trying to create a document from a non supported spec version', function() { const doc = { asyncapi: "99.99.99" }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(() => newAsyncAPIDocument(detailed)).toThrow("Unsupported AsyncAPI version: 99.99.99"); }); }); diff --git a/test/models/base.spec.ts b/test/models/base.spec.ts index eb101e38f..34718fc0f 100644 --- a/test/models/base.spec.ts +++ b/test/models/base.spec.ts @@ -19,7 +19,7 @@ describe('Base model', function() { it('should return the value of a given key, even when this is falsy', function() { const doc = { 0: 'testing' }; const d = new Model(doc); - expect(d.json(0)).toEqual(doc[0]); + expect(d.json('0')).toEqual(doc[0]); }); }); }); diff --git a/test/models/v2/asyncapi.spec.ts b/test/models/v2/asyncapi.spec.ts index e8033f091..303262973 100644 --- a/test/models/v2/asyncapi.spec.ts +++ b/test/models/v2/asyncapi.spec.ts @@ -4,24 +4,23 @@ import { Components } from '../../../src/models/v2/components'; import { Info } from '../../../src/models/v2/info'; import { Messages } from '../../../src/models/v2/messages'; import { Operations } from '../../../src/models/v2/operations'; -import { Schemas } from '../../../src/models/v2/schemas'; import { SecuritySchemes } from '../../../src/models/v2/security-schemes'; import { Servers } from '../../../src/models/v2/servers'; -import { - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('AsyncAPIDocument model', function() { describe('.version()', function() { it('should return the value', function() { - const doc = { asyncapi: "2.0.0" }; + const doc = serializeInput({ asyncapi: "2.0.0" }); const d = new AsyncAPIDocument(doc); expect(d.version()).toEqual(doc.asyncapi); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.version()).toBeUndefined(); }); @@ -29,13 +28,13 @@ describe('AsyncAPIDocument model', function() { describe('.hasDefaultContentType()', function() { it('should return true when there is a value', function() { - const doc = { defaultContentType: "..." }; + const doc = serializeInput({ defaultContentType: "..." }); const d = new AsyncAPIDocument(doc); expect(d.hasDefaultContentType()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.hasDefaultContentType()).toEqual(false); }); @@ -43,13 +42,13 @@ describe('AsyncAPIDocument model', function() { describe('.defaultContentType()', function() { it('should return the value', function() { - const doc = { defaultContentType: "..." }; + const doc = serializeInput({ defaultContentType: "..." }); const d = new AsyncAPIDocument(doc); expect(d.defaultContentType()).toEqual(doc.defaultContentType); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.defaultContentType()).toBeUndefined(); }); @@ -57,7 +56,7 @@ describe('AsyncAPIDocument model', function() { describe('.info()', function() { it('should return an Info object', function() { - const doc = { info: { name: "LeChuck" } }; + const doc = serializeInput({ info: {} }); const d = new AsyncAPIDocument(doc); expect(d.info()).toBeInstanceOf(Info); }); @@ -65,7 +64,7 @@ describe('AsyncAPIDocument model', function() { describe('.servers()', function() { it('should return a collection of servers', function() { - const doc = { servers: { development: {} } }; + const doc = serializeInput({ servers: { development: {} } }); const d = new AsyncAPIDocument(doc); expect(d.servers()).toBeInstanceOf(Servers); expect(d.servers()).toHaveLength(1); @@ -73,7 +72,7 @@ describe('AsyncAPIDocument model', function() { }) it('should return a collection of servers even if servers are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.servers()).toBeInstanceOf(Servers); }) @@ -81,7 +80,7 @@ describe('AsyncAPIDocument model', function() { describe('.channels()', function() { it('should return a collection of channels', function() { - const doc = { channels: { 'user/signup': {} } }; + const doc = serializeInput({ channels: { 'user/signup': {} } }); const d = new AsyncAPIDocument(doc); expect(d.channels()).toBeInstanceOf(Channels); expect(d.channels()).toHaveLength(1); @@ -89,7 +88,7 @@ describe('AsyncAPIDocument model', function() { }) it('should return a collection of channels even if channels are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.channels()).toBeInstanceOf(Channels); }) @@ -97,14 +96,14 @@ describe('AsyncAPIDocument model', function() { describe('.operations()', function() { it('should return a collection of operations', function() { - const doc = { channels: { 'user/signup': { publish: {}, subscribe: {} }, 'user/logout': { publish: {} } } }; + const doc = serializeInput({ channels: { 'user/signup': { publish: {}, subscribe: {} }, 'user/logout': { publish: {} } } }); const d = new AsyncAPIDocument(doc); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations()).toHaveLength(3); }) it('should return a collection of operations even if operations are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.operations()).toBeInstanceOf(Operations); }) @@ -112,14 +111,14 @@ describe('AsyncAPIDocument model', function() { describe('.messages()', function() { it('should return a collection of messages', function() { - const doc = { channels: { 'user/signup': { publish: { message: {} }, subscribe: { message: { oneOf: [{}, {}] } } }, 'user/logout': { publish: { message: {} } } } }; + const doc = serializeInput({ channels: { 'user/signup': { publish: { message: {} }, subscribe: { message: { oneOf: [{}, {}] } } }, 'user/logout': { publish: { message: {} } } } }); const d = new AsyncAPIDocument(doc); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages()).toHaveLength(4); }) it('should return a collection of messages even if messages are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.messages()).toBeInstanceOf(Messages); }) @@ -131,14 +130,14 @@ describe('AsyncAPIDocument model', function() { describe('.securitySchemes()', function() { it('should return a collection of securitySchemes', function() { - const doc = { components: { securitySchemes: { security1: {}, security2: {} } } }; + const doc = serializeInput({ components: { securitySchemes: { security1: { type: 'X509' }, security2: { type: 'apiKey' } } } }); const d = new AsyncAPIDocument(doc); expect(d.securitySchemes()).toBeInstanceOf(SecuritySchemes); expect(d.securitySchemes()).toHaveLength(2); }) it('should return a collection of securitySchemes even if securitySchemes are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.securitySchemes()).toBeInstanceOf(SecuritySchemes); }) @@ -146,19 +145,19 @@ describe('AsyncAPIDocument model', function() { describe('.components()', function() { it('should return a components model', function() { - const doc = { components: {} }; + const doc = serializeInput({ components: {} }); const d = new AsyncAPIDocument(doc); expect(d.components()).toBeInstanceOf(Components); }) it('should return a components model even if components are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new AsyncAPIDocument(doc); expect(d.components()).toBeInstanceOf(Components); }) }) - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(AsyncAPIDocument); + describe('mixins', function() { + assertExtensions(AsyncAPIDocument); }); }); diff --git a/test/models/v2/channel-parameter.spec.ts b/test/models/v2/channel-parameter.spec.ts index 1c1a4a074..9a7816504 100644 --- a/test/models/v2/channel-parameter.spec.ts +++ b/test/models/v2/channel-parameter.spec.ts @@ -1,15 +1,14 @@ import { ChannelParameter } from '../../../src/models/v2/channel-parameter'; import { Schema } from '../../../src/models/v2/schema'; -import { - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('ChannelParameter model', function() { describe('.id()', function() { it('should return id of model', function() { - const doc = {}; + const doc = serializeInput({}); const d = new ChannelParameter(doc, { asyncapi: {} as any, pointer: '', id: 'parameter' }); expect(d.id()).toEqual('parameter'); }); @@ -17,13 +16,13 @@ describe('ChannelParameter model', function() { describe('.hasLocation()', function() { it('should return true when there is a value', function() { - const doc = { location: "..." }; + const doc = serializeInput({ location: "..." }); const d = new ChannelParameter(doc); expect(d.hasLocation()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new ChannelParameter(doc); expect(d.hasLocation()).toEqual(false); }); @@ -31,13 +30,13 @@ describe('ChannelParameter model', function() { describe('.location()', function() { it('should return the value', function() { - const doc = { location: "..." }; + const doc = serializeInput({ location: "..." }); const d = new ChannelParameter(doc); expect(d.location()).toEqual(doc.location); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new ChannelParameter(doc); expect(d.location()).toBeUndefined(); }); @@ -45,13 +44,13 @@ describe('ChannelParameter model', function() { describe('.hasSchema()', function() { it('should return true when there is a value', function() { - const doc = { schema: {} }; + const doc = serializeInput({ schema: {} }); const d = new ChannelParameter(doc); expect(d.hasSchema()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new ChannelParameter(doc); expect(d.hasSchema()).toEqual(false); }); @@ -59,20 +58,20 @@ describe('ChannelParameter model', function() { describe('.schema()', function() { it('should return the value', function() { - const doc = { schema: {} }; + const doc = serializeInput({ schema: {} }); const d = new ChannelParameter(doc); expect(d.schema()).toBeInstanceOf(Schema); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new ChannelParameter(doc); expect(d.schema()).toBeUndefined(); }); }); - describe('mixins inheritance', function() { - assertDescriptionMixinInheritance(ChannelParameter); - assertExtensionsMixinInheritance(ChannelParameter); + describe('mixins', function() { + assertDescription(ChannelParameter); + assertExtensions(ChannelParameter); }); }); diff --git a/test/models/v2/channel.spec.ts b/test/models/v2/channel.spec.ts index 3e872663e..279969a8d 100644 --- a/test/models/v2/channel.spec.ts +++ b/test/models/v2/channel.spec.ts @@ -8,16 +8,14 @@ import { Message } from '../../../src/models/v2/message'; import { Servers } from '../../../src/models/v2/servers'; import { Server } from '../../../src/models/v2/server'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertBindings, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('Channel model', function() { describe('.id()', function() { it('should return id of model', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Channel(doc, { asyncapi: {} as any, pointer: '', id: 'channel', address: '' }); expect(d.id()).toEqual('channel'); }); @@ -25,7 +23,7 @@ describe('Channel model', function() { describe('.address()', function() { it('should return the value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Channel(doc, { asyncapi: {} as any, pointer: '', id: 'channel', address: 'user/signup' }); expect(d.address()).toEqual('user/signup'); }); @@ -33,7 +31,7 @@ describe('Channel model', function() { describe('.servers()', function() { it('should return collection of servers - available on all servers', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Channel(doc, { asyncapi: { parsed: { servers: { someServer1: {}, someServer2: {}, } } } as any, pointer: '', id: 'channel', address: 'user/signup' }); expect(d.servers()).toBeInstanceOf(Servers); expect(d.servers().all()).toHaveLength(2); @@ -44,7 +42,7 @@ describe('Channel model', function() { }); it('should return collection of servers - available on all servers (empty servers array)', function() { - const doc = { servers: [] }; + const doc = serializeInput({ servers: [] }); const d = new Channel(doc, { asyncapi: { parsed: { servers: { someServer1: {}, someServer2: {}, } } } as any, pointer: '', id: 'channel', address: 'user/signup' }); expect(d.servers()).toBeInstanceOf(Servers); expect(d.servers().all()).toHaveLength(2); @@ -55,7 +53,7 @@ describe('Channel model', function() { }); it('should return collection of servers - available only on particular ones', function() { - const doc = { servers: ['someServer2'] }; + const doc = serializeInput({ servers: ['someServer2'] }); const d = new Channel(doc, { asyncapi: { parsed: { servers: { someServer1: {}, someServer2: {}, } } } as any, pointer: '', id: 'channel', address: 'user/signup' }); expect(d.servers()).toBeInstanceOf(Servers); expect(d.servers().all()).toHaveLength(1); @@ -66,7 +64,7 @@ describe('Channel model', function() { describe('.operations()', function() { it('should return collection of operations - publish operation', function() { - const doc = { publish: {} }; + const doc = serializeInput({ publish: {} }); const d = new Channel(doc); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(1); @@ -75,7 +73,7 @@ describe('Channel model', function() { }); it('should return collection of operations - subscribe operation', function() { - const doc = { subscribe: {} }; + const doc = serializeInput({ subscribe: {} }); const d = new Channel(doc); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(1); @@ -84,7 +82,7 @@ describe('Channel model', function() { }); it('should return collection of operations - both operations', function() { - const doc = { publish: {}, subscribe: {} }; + const doc = serializeInput({ publish: {}, subscribe: {} }); const d = new Channel(doc); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(2); @@ -97,27 +95,27 @@ describe('Channel model', function() { describe('.messages()', function() { it('should return collection of messages - single message', function() { - const doc = { publish: { message: { messageId: '...' } } }; + const doc = serializeInput({ publish: { message: { messageId: '...' } } }); const d = new Channel(doc); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(1); expect(d.messages().all()[0]).toBeInstanceOf(Message); - expect(d.messages().all()[0].messageId()).toEqual(doc.publish.message.messageId); + expect(d.messages().all()[0].messageId()).toEqual((doc as any).publish.message.messageId); }); it('should return collection of messages - oneOf message', function() { - const doc = { subscribe: { message: { oneOf: [{ messageId: '1' }, { messageId: '2' }] } } }; + const doc = serializeInput({ subscribe: { message: { oneOf: [{ messageId: '1' }, { messageId: '2' }] } } }); const d = new Channel(doc); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(2); expect(d.messages().all()[0]).toBeInstanceOf(Message); - expect(d.messages().all()[0].messageId()).toEqual(doc.subscribe.message.oneOf[0].messageId); + expect(d.messages().all()[0].messageId()).toEqual((doc as any).subscribe.message.oneOf[0].messageId); expect(d.messages().all()[1]).toBeInstanceOf(Message); - expect(d.messages().all()[1].messageId()).toEqual(doc.subscribe.message.oneOf[1].messageId); + expect(d.messages().all()[1].messageId()).toEqual((doc as any).subscribe.message.oneOf[1].messageId); }); it('should return collection of messages - single message and oneOf', function() { - const doc = { publish: { message: {} }, subscribe: { message: { oneOf: [{ messageId: '1' }, { messageId: '2' }] } } }; + const doc = serializeInput({ publish: { message: {} }, subscribe: { message: { oneOf: [{ messageId: '1' }, { messageId: '2' }] } } }); const d = new Channel(doc); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(3); @@ -126,7 +124,7 @@ describe('Channel model', function() { describe('.parameters()', function() { it('should return collection of channel parameters', function() { - const doc = { parameters: { parameter1: {}, parameter2: {} } }; + const doc = serializeInput({ parameters: { parameter1: {}, parameter2: {} } }); const d = new Channel(doc); expect(d.parameters()).toBeInstanceOf(ChannelParameters); expect(d.parameters().all()).toHaveLength(2); @@ -137,9 +135,9 @@ describe('Channel model', function() { }); }); - describe('mixins inheritance', function() { - assertBindingsMixinInheritance(Channel); - assertDescriptionMixinInheritance(Channel); - assertExtensionsMixinInheritance(Channel); + describe('mixins', function() { + assertBindings(Channel); + assertDescription(Channel); + assertExtensions(Channel); }); }); diff --git a/test/models/v2/components.spec.ts b/test/models/v2/components.spec.ts index 6265ed543..c550f7a1f 100644 --- a/test/models/v2/components.spec.ts +++ b/test/models/v2/components.spec.ts @@ -1,5 +1,5 @@ import { Components } from '../../../src/models/v2/components'; -import { Bindings } from '../../../src/models/v2/mixins/bindings'; +import { Bindings } from '../../../src/models/v2/bindings'; import { Channel } from '../../../src/models/v2/channel'; import { ChannelParameter } from '../../../src/models/v2/channel-parameter'; import { CorrelationId } from '../../../src/models/v2/correlation-id'; @@ -11,14 +11,14 @@ import { Server } from '../../../src/models/v2/server'; import { ServerVariable } from '../../../src/models/v2/server-variable'; import { SecurityScheme } from '../../../src/models/v2/security-scheme'; -import { - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('Components model', function() { describe('.servers()', function() { it('should return map of servers', function() { - const doc = { servers: { server: {} } }; + const doc = serializeInput({ servers: { server: {} } }); const d = new Components(doc); expect(typeof d.servers()).toEqual('object'); expect(Object.keys(d.servers())).toHaveLength(1); @@ -26,7 +26,7 @@ describe('Components model', function() { }); it('should return empty map when servers are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.servers()).toEqual('object'); expect(Object.keys(d.servers())).toHaveLength(0); @@ -35,7 +35,7 @@ describe('Components model', function() { describe('.channels()', function() { it('should return map of channels', function() { - const doc = { channels: { channel: {} } }; + const doc = serializeInput({ channels: { channel: {} } }); const d = new Components(doc); expect(typeof d.channels()).toEqual('object'); expect(Object.keys(d.channels())).toHaveLength(1); @@ -43,7 +43,7 @@ describe('Components model', function() { }); it('should return empty map when channels are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.channels()).toEqual('object'); expect(Object.keys(d.channels())).toHaveLength(0); @@ -52,7 +52,7 @@ describe('Components model', function() { describe('.messages()', function() { it('should return map of messages', function() { - const doc = { messages: { message: {} } }; + const doc = serializeInput({ messages: { message: {} } }); const d = new Components(doc); expect(typeof d.messages()).toEqual('object'); expect(Object.keys(d.messages())).toHaveLength(1); @@ -60,7 +60,7 @@ describe('Components model', function() { }); it('should return empty map when messages are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.messages()).toEqual('object'); expect(Object.keys(d.messages())).toHaveLength(0); @@ -69,7 +69,7 @@ describe('Components model', function() { describe('.schemas()', function() { it('should return map of schemas', function() { - const doc = { schemas: { schema: {} } }; + const doc = serializeInput({ schemas: { schema: {} } }); const d = new Components(doc); expect(typeof d.schemas()).toEqual('object'); expect(Object.keys(d.schemas())).toHaveLength(1); @@ -77,7 +77,7 @@ describe('Components model', function() { }); it('should return empty map when schemas are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.schemas()).toEqual('object'); expect(Object.keys(d.schemas())).toHaveLength(0); @@ -86,7 +86,7 @@ describe('Components model', function() { describe('.channelParameters()', function() { it('should return map of channelParameters', function() { - const doc = { parameters: { parameter: {} } }; + const doc = serializeInput({ parameters: { parameter: {} } }); const d = new Components(doc); expect(typeof d.channelParameters()).toEqual('object'); expect(Object.keys(d.channelParameters())).toHaveLength(1); @@ -94,7 +94,7 @@ describe('Components model', function() { }); it('should return empty map when channelParameters are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.channelParameters()).toEqual('object'); expect(Object.keys(d.channelParameters())).toHaveLength(0); @@ -103,7 +103,7 @@ describe('Components model', function() { describe('.serverVariables()', function() { it('should return map of serverVariables', function() { - const doc = { serverVariables: { variable: {} } }; + const doc = serializeInput({ serverVariables: { variable: {} } }); const d = new Components(doc); expect(typeof d.serverVariables()).toEqual('object'); expect(Object.keys(d.serverVariables())).toHaveLength(1); @@ -111,7 +111,7 @@ describe('Components model', function() { }); it('should return empty map when serverVariables are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.serverVariables()).toEqual('object'); expect(Object.keys(d.serverVariables())).toHaveLength(0); @@ -120,7 +120,7 @@ describe('Components model', function() { describe('.operationTraits()', function() { it('should return map of operationTraits', function() { - const doc = { operationTraits: { trait: {} } }; + const doc = serializeInput({ operationTraits: { trait: {} } }); const d = new Components(doc); expect(typeof d.operationTraits()).toEqual('object'); expect(Object.keys(d.operationTraits())).toHaveLength(1); @@ -128,7 +128,7 @@ describe('Components model', function() { }); it('should return empty map when operationTraits are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.operationTraits()).toEqual('object'); expect(Object.keys(d.operationTraits())).toHaveLength(0); @@ -137,7 +137,7 @@ describe('Components model', function() { describe('.messageTraits()', function() { it('should return map of messageTraits', function() { - const doc = { messageTraits: { trait: {} } }; + const doc = serializeInput({ messageTraits: { trait: {} } }); const d = new Components(doc); expect(typeof d.messageTraits()).toEqual('object'); expect(Object.keys(d.messageTraits())).toHaveLength(1); @@ -145,7 +145,7 @@ describe('Components model', function() { }); it('should return empty map when messageTraits are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.messageTraits()).toEqual('object'); expect(Object.keys(d.messageTraits())).toHaveLength(0); @@ -154,7 +154,7 @@ describe('Components model', function() { describe('.correlationIds()', function() { it('should return map of correlationIds', function() { - const doc = { correlationIds: { id: {} } }; + const doc = serializeInput({ correlationIds: { id: {} } }); const d = new Components(doc); expect(typeof d.correlationIds()).toEqual('object'); expect(Object.keys(d.correlationIds())).toHaveLength(1); @@ -162,7 +162,7 @@ describe('Components model', function() { }); it('should return empty map when correlationIds are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.correlationIds()).toEqual('object'); expect(Object.keys(d.correlationIds())).toHaveLength(0); @@ -171,7 +171,7 @@ describe('Components model', function() { describe('.securitySchemes()', function() { it('should return map of securitySchemes', function() { - const doc = { securitySchemes: { scheme: {} } }; + const doc = serializeInput({ securitySchemes: { scheme: {} } }); const d = new Components(doc); expect(typeof d.securitySchemes()).toEqual('object'); expect(Object.keys(d.securitySchemes())).toHaveLength(1); @@ -179,7 +179,7 @@ describe('Components model', function() { }); it('should return empty map when securitySchemes are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.securitySchemes()).toEqual('object'); expect(Object.keys(d.securitySchemes())).toHaveLength(0); @@ -188,7 +188,7 @@ describe('Components model', function() { describe('.serverBindings()', function() { it('should return map of serverBindings', function() { - const doc = { serverBindings: { bidning: {} } }; + const doc = serializeInput({ serverBindings: { bidning: {} } }); const d = new Components(doc); expect(typeof d.serverBindings()).toEqual('object'); expect(Object.keys(d.serverBindings())).toHaveLength(1); @@ -196,7 +196,7 @@ describe('Components model', function() { }); it('should return empty map when serverBindings are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.serverBindings()).toEqual('object'); expect(Object.keys(d.serverBindings())).toHaveLength(0); @@ -205,7 +205,7 @@ describe('Components model', function() { describe('.channelBindings()', function() { it('should return map of channelBindings', function() { - const doc = { channelBindings: { bidning: {} } }; + const doc = serializeInput({ channelBindings: { bidning: {} } }); const d = new Components(doc); expect(typeof d.channelBindings()).toEqual('object'); expect(Object.keys(d.channelBindings())).toHaveLength(1); @@ -213,7 +213,7 @@ describe('Components model', function() { }); it('should return empty map when channelBindings are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.channelBindings()).toEqual('object'); expect(Object.keys(d.channelBindings())).toHaveLength(0); @@ -222,7 +222,7 @@ describe('Components model', function() { describe('.operationBindings()', function() { it('should return map of operationBindings', function() { - const doc = { operationBindings: { bidning: {} } }; + const doc = serializeInput({ operationBindings: { bidning: {} } }); const d = new Components(doc); expect(typeof d.operationBindings()).toEqual('object'); expect(Object.keys(d.operationBindings())).toHaveLength(1); @@ -230,7 +230,7 @@ describe('Components model', function() { }); it('should return empty map when operationBindings are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.operationBindings()).toEqual('object'); expect(Object.keys(d.operationBindings())).toHaveLength(0); @@ -239,7 +239,7 @@ describe('Components model', function() { describe('.messageBindings()', function() { it('should return map of messageBindings', function() { - const doc = { messageBindings: { bidning: {} } }; + const doc = serializeInput({ messageBindings: { bidning: {} } }); const d = new Components(doc); expect(typeof d.messageBindings()).toEqual('object'); expect(Object.keys(d.messageBindings())).toHaveLength(1); @@ -247,14 +247,14 @@ describe('Components model', function() { }); it('should return empty map when messageBindings are not defined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Components(doc); expect(typeof d.messageBindings()).toEqual('object'); expect(Object.keys(d.messageBindings())).toHaveLength(0); }); }); - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(Components); + describe('mixins', function() { + assertExtensions(Components); }); }); diff --git a/test/models/v2/contact.spec.ts b/test/models/v2/contact.spec.ts index b7b9a8bb6..a3f3d7c89 100644 --- a/test/models/v2/contact.spec.ts +++ b/test/models/v2/contact.spec.ts @@ -1,19 +1,19 @@ import { Contact } from '../../../src/models/v2/contact'; -import { - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('Contact model', function() { describe('.hasName()', function() { it('should return true when there is a value', function() { - const doc = { name: "LeChuck" }; + const doc = serializeInput({ name: "LeChuck" }); const d = new Contact(doc); expect(d.hasName()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.hasName()).toEqual(false); }); @@ -21,13 +21,13 @@ describe('Contact model', function() { describe('.name()', function() { it('should return the value', function() { - const doc = { name: "LeChuck" }; + const doc = serializeInput({ name: "LeChuck" }); const d = new Contact(doc); expect(d.name()).toEqual(doc.name); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.name()).toBeUndefined(); }); @@ -35,13 +35,13 @@ describe('Contact model', function() { describe('.hasUrl()', function() { it('should return true when there is a value', function() { - const doc = { url: "https://example.com" }; + const doc = serializeInput({ url: "https://example.com" }); const d = new Contact(doc); expect(d.hasUrl()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.hasUrl()).toEqual(false); }); @@ -49,13 +49,13 @@ describe('Contact model', function() { describe('.url()', function() { it('should return the value', function() { - const doc = { url: "https://example.com" }; + const doc = serializeInput({ url: "https://example.com" }); const d = new Contact(doc); expect(d.url()).toEqual(doc.url); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.url()).toBeUndefined(); }); @@ -63,13 +63,13 @@ describe('Contact model', function() { describe('.hasEmail()', function() { it('should return true when there is a value', function() { - const doc = { email: "lechuck@example.com" }; + const doc = serializeInput({ email: "lechuck@example.com" }); const d = new Contact(doc); expect(d.hasEmail()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.hasEmail()).toEqual(false); }); @@ -77,19 +77,19 @@ describe('Contact model', function() { describe('.email()', function() { it('should return the value', function() { - const doc = { email: "lechuck@example.com" }; + const doc = serializeInput({ email: "lechuck@example.com" }); const d = new Contact(doc); expect(d.email()).toEqual(doc.email); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = serializeInput({}); const d = new Contact(doc); expect(d.email()).toBeUndefined(); }); }); - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(Contact); + describe('mixins', function() { + assertExtensions(Contact); }); }); diff --git a/test/models/v2/correlation-id.spec.ts b/test/models/v2/correlation-id.spec.ts index 18b4e3918..f0653b722 100644 --- a/test/models/v2/correlation-id.spec.ts +++ b/test/models/v2/correlation-id.spec.ts @@ -1,20 +1,19 @@ import { CorrelationId } from '../../../src/models/v2/correlation-id'; -import { - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('CorrelationId model', function() { describe('.hasLocation()', function() { it('should return true when there is a value', function() { - const doc = { location: "..." }; + const doc = serializeInput({ location: "..." }); const d = new CorrelationId(doc); expect(d.hasLocation()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new CorrelationId(doc); expect(d.hasLocation()).toEqual(false); }); @@ -22,20 +21,20 @@ describe('CorrelationId model', function() { describe('.location()', function() { it('should return the value', function() { - const doc = { location: "..." }; + const doc = serializeInput({ location: "..." }); const d = new CorrelationId(doc); expect(d.location()).toEqual(doc.location); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new CorrelationId(doc); expect(d.location()).toBeUndefined(); }); }); - describe('mixins inheritance', function() { - assertDescriptionMixinInheritance(CorrelationId); - assertExtensionsMixinInheritance(CorrelationId); + describe('mixins', function() { + assertDescription(CorrelationId); + assertExtensions(CorrelationId); }); }); diff --git a/test/models/v2/external-docs.spec.ts b/test/models/v2/external-docs.spec.ts index 579247c5a..a610380ef 100644 --- a/test/models/v2/external-docs.spec.ts +++ b/test/models/v2/external-docs.spec.ts @@ -1,21 +1,20 @@ -import { ExternalDocumentation } from '../../../src/models/v2/mixins/external-docs'; +import { ExternalDocumentation } from '../../../src/models/v2/external-docs'; -import { - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('ExternalDocumentation model', function() { describe('.name()', function() { it('should return the value', function() { - const doc = { url: 'somewhere' }; + const doc = serializeInput({ url: 'somewhere' }); const d = new ExternalDocumentation(doc); expect(d.url()).toEqual(doc.url); }); }); - describe('mixins inheritance', function() { - assertDescriptionMixinInheritance(ExternalDocumentation); - assertExtensionsMixinInheritance(ExternalDocumentation); + describe('mixins', function() { + assertDescription(ExternalDocumentation); + assertExtensions(ExternalDocumentation); }); }); diff --git a/test/models/v2/info.spec.ts b/test/models/v2/info.spec.ts index 042f60c2e..701e27db6 100644 --- a/test/models/v2/info.spec.ts +++ b/test/models/v2/info.spec.ts @@ -1,19 +1,19 @@ import { Info } from '../../../src/models/v2/info'; import { Contact } from '../../../src/models/v2/contact'; import { License } from '../../../src/models/v2/license'; -import { ExternalDocumentation } from '../../../src/models/v2/mixins/external-docs'; -import { Tags, Tag } from '../../../src/models/v2/mixins/tags'; +import { ExternalDocumentation } from '../../../src/models/v2/external-docs'; +import { Tags } from '../../../src/models/v2/tags'; +import { Tag } from '../../../src/models/v2/tag'; import { createDetailedAsyncAPI } from '../../../src/utils'; -import { - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('Info model', function() { describe('.title()', function() { it('should return the value', function() { - const doc = { title: "Example API" }; + const doc = serializeInput({ title: "Example API" }); const d = new Info(doc); expect(d.title()).toEqual(doc.title); }); @@ -21,7 +21,7 @@ describe('Info model', function() { describe('.version()', function() { it('should return the value', function() { - const doc = { version: "1.0.0" }; + const doc = serializeInput({ version: "1.0.0" }); const d = new Info(doc); expect(d.version()).toEqual(doc.version); }); @@ -30,15 +30,15 @@ describe('Info model', function() { describe('.hasId()', function() { it('should return true when there is a value', function() { const doc = { asyncapi: '2.0.0', id: 'someId' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.hasId()).toEqual(true); }); it('should return false when there is no value', function() { const doc = { asyncapi: '2.0.0' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.hasId()).toEqual(false); }); }); @@ -46,28 +46,28 @@ describe('Info model', function() { describe('.id()', function() { it('should return the value', function() { const doc = { asyncapi: '2.0.0', id: 'someId' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.id()).toEqual(doc.id); }); it('should return undefined when there is no value', function() { const doc = { asyncapi: '2.0.0' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.id()).toEqual(undefined); }); }); describe('.hasTermsOfService()', function() { it('should return true when there is a value', function() { - const doc = { termsOfService: "These are the terms of service" }; + const doc = serializeInput({ termsOfService: "These are the terms of service" }); const d = new Info(doc); expect(d.hasTermsOfService()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.hasTermsOfService()).toEqual(false); }); @@ -75,13 +75,13 @@ describe('Info model', function() { describe('.termsOfService()', function() { it('should return the value', function() { - const doc = { termsOfService: "These are the terms of service" }; + const doc = serializeInput({ termsOfService: "These are the terms of service" }); const d = new Info(doc); expect(d.termsOfService()).toEqual(doc.termsOfService); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.termsOfService()).toBeUndefined(); }); @@ -89,13 +89,13 @@ describe('Info model', function() { describe('.hasContact()', function() { it('should return true when there is a value', function() { - const doc = { contact: { name: "LeChuck" } }; + const doc = serializeInput({ contact: { name: "LeChuck" } }); const d = new Info(doc); expect(d.hasContact()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.hasContact()).toEqual(false); }); @@ -103,13 +103,13 @@ describe('Info model', function() { describe('.contact()', function() { it('should return a Contact object', function() { - const doc = { contact: { name: "LeChuck" } }; + const doc = serializeInput({ contact: { name: "LeChuck" } }); const d = new Info(doc); expect(d.contact()).toBeInstanceOf(Contact); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.contact()).toBeUndefined(); }); @@ -117,13 +117,13 @@ describe('Info model', function() { describe('.hasLicense()', function() { it('should return true when there is a value', function() { - const doc = { license: { name: "Apache 2.0" } }; + const doc = serializeInput({ license: { name: "Apache 2.0" } }); const d = new Info(doc); expect(d.hasLicense()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.hasLicense()).toEqual(false); }); @@ -131,13 +131,13 @@ describe('Info model', function() { describe('.license()', function() { it('should return a License object', function() { - const doc = { license: { name: "Apache 2.0" } }; + const doc = serializeInput({ license: { name: "Apache 2.0" } }); const d = new Info(doc); expect(d.license()).toBeInstanceOf(License); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Info(doc); expect(d.license()).toBeUndefined(); }); @@ -146,22 +146,22 @@ describe('Info model', function() { describe('.hasExternalDocs()', function() { it('should return true when there is a value', function() { const doc = { asyncapi: '2.0.0', externalDocs: { url: 'https://example.com' } }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.hasExternalDocs()).toEqual(true); }); it('should return false when there is an empty object', function() { const doc = { asyncapi: '2.0.0', externalDocs: {} }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.hasExternalDocs()).toEqual(false); }); it('should return false when there is no value', function() { const doc = { asyncapi: '2.0.0' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.hasExternalDocs()).toEqual(false); }); }); @@ -169,23 +169,23 @@ describe('Info model', function() { describe('.externalDocs()', function() { it('should return the value', function() { const doc = { asyncapi: '2.0.0', externalDocs: { url: 'https://example.com' } }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.externalDocs()).toBeInstanceOf(ExternalDocumentation); expect(d.externalDocs()!.json()).toEqual(doc.externalDocs); }); it('should return undefined when there is an empty object', function() { const doc = { asyncapi: '2.0.0', externalDocs: {} }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.externalDocs()).toEqual(undefined); }); it('should return undefined when there is no value', function() { const doc = { asyncapi: '2.0.0' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.externalDocs()).toEqual(undefined); }); }); @@ -194,8 +194,8 @@ describe('Info model', function() { it('should return the collection of tags', function() { const tags = [{ name: 'one' }, { name: 'two' }]; const doc = { asyncapi: '2.0.0', tags }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.tags()).toBeInstanceOf(Tags); expect(d.tags().length).toEqual(2); expect(d.tags().all()[0]).toBeInstanceOf(Tag); @@ -204,15 +204,15 @@ describe('Info model', function() { it('should return empty array when there is an empty collection', function() { const doc = { asyncapi: '2.0.0' }; - const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, pointer: '/info' }); + const asyncapi = createDetailedAsyncAPI(doc, doc as any); + const d = new Info(serializeInput({}), { asyncapi, pointer: '/info' }); expect(d.tags()).toBeInstanceOf(Tags); expect(d.tags().all()).toEqual([]); }); }); - describe('mixins inheritance', function() { - assertDescriptionMixinInheritance(Info); - assertExtensionsMixinInheritance(Info); + describe('mixins', function() { + assertDescription(Info); + assertExtensions(Info); }); }); diff --git a/test/models/v2/license.spec.ts b/test/models/v2/license.spec.ts index 855ba320a..260e0a3c2 100644 --- a/test/models/v2/license.spec.ts +++ b/test/models/v2/license.spec.ts @@ -1,13 +1,13 @@ import { License } from '../../../src/models/v2/license'; -import { - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('License model', function() { describe('.name()', function() { it('should return the value', function() { - const doc = { name: "Apache 2.0" }; + const doc = serializeInput({ name: "Apache 2.0" }); const d = new License(doc); expect(d.name()).toEqual(doc.name); }); @@ -15,13 +15,13 @@ describe('License model', function() { describe('.hasUrl()', function() { it('should return true when there is a value', function() { - const doc = { url: "https://www.apache.org/licenses/LICENSE-2.0.html" }; + const doc = serializeInput({ url: "https://www.apache.org/licenses/LICENSE-2.0.html" }); const d = new License(doc); expect(d.hasUrl()).toEqual(true); }); it('should return false when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new License(doc); expect(d.hasUrl()).toEqual(false); }); @@ -29,19 +29,19 @@ describe('License model', function() { describe('.url()', function() { it('should return the value', function() { - const doc = { url: "https://www.apache.org/licenses/LICENSE-2.0.html" }; + const doc = serializeInput({ url: "https://www.apache.org/licenses/LICENSE-2.0.html" }); const d = new License(doc); expect(d.url()).toEqual(doc.url); }); it('should return undefined when there is no value', function() { - const doc = {}; + const doc = serializeInput({}); const d = new License(doc); expect(d.url()).toBeUndefined(); }); }); - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(License); + describe('mixins', function() { + assertExtensions(License); }); }); diff --git a/test/models/v2/message-example.spec.ts b/test/models/v2/message-example.spec.ts index bc41f171b..a4a39468a 100644 --- a/test/models/v2/message-example.spec.ts +++ b/test/models/v2/message-example.spec.ts @@ -1,8 +1,6 @@ import { MessageExample } from '../../../src/models/v2/message-example'; -import { - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { assertExtensions } from './utils'; describe('MessageExample model', function() { describe('.hasName()', function() { @@ -117,7 +115,7 @@ describe('MessageExample model', function() { }); }); - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(MessageExample); + describe('mixins', function() { + assertExtensions(MessageExample); }); }); diff --git a/test/models/v2/message-trait.spec.ts b/test/models/v2/message-trait.spec.ts index 14b236f92..6357f5aa3 100644 --- a/test/models/v2/message-trait.spec.ts +++ b/test/models/v2/message-trait.spec.ts @@ -4,13 +4,7 @@ import { MessageExample } from '../../../src/models/v2/message-example'; import { MessageTrait } from '../../../src/models/v2/message-trait'; import { Schema } from '../../../src/models/v2/schema'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance, - assertTagsMixinInheritance, -} from './mixins/inheritance'; +import { assertBindings, assertDescription, assertExtensions, assertExternalDocumentation, assertTags } from './utils'; describe('MessageTrait model', function() { describe('.id()', function() { @@ -71,7 +65,7 @@ describe('MessageTrait model', function() { describe('.hasCorrelationId()', function() { it('should return true when there is a value', function() { - const doc = { correlationId: {} }; + const doc = { correlationId: { location: '...' } }; const d = new MessageTrait(doc); expect(d.hasCorrelationId()).toEqual(true); }); @@ -85,7 +79,7 @@ describe('MessageTrait model', function() { describe('.correlationId()', function() { it('should return the value', function() { - const doc = { correlationId: {} }; + const doc = { correlationId: { location: '...' } }; const d = new MessageTrait(doc); expect(d.correlationId()).toBeInstanceOf(CorrelationId); }); @@ -260,11 +254,11 @@ describe('MessageTrait model', function() { }); }); - describe('mixins inheritance', function() { - assertBindingsMixinInheritance(MessageTrait); - assertDescriptionMixinInheritance(MessageTrait); - assertExtensionsMixinInheritance(MessageTrait); - assertExternalDocumentationMixinInheritance(MessageTrait); - assertTagsMixinInheritance(MessageTrait); + describe('mixins', function() { + assertBindings(MessageTrait); + assertDescription(MessageTrait); + assertExtensions(MessageTrait); + assertExternalDocumentation(MessageTrait); + assertTags(MessageTrait); }); }); diff --git a/test/models/v2/message.spec.ts b/test/models/v2/message.spec.ts index dbe19266a..cb82189d1 100644 --- a/test/models/v2/message.spec.ts +++ b/test/models/v2/message.spec.ts @@ -9,13 +9,7 @@ import { Schema } from '../../../src/models/v2/schema'; import { Servers } from '../../../src/models/v2/servers'; import { Server } from '../../../src/models/v2/server'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance, - assertTagsMixinInheritance, -} from './mixins/inheritance'; +import { assertBindings, assertDescription, assertExtensions, assertExternalDocumentation, assertTags } from './utils'; describe('Message model', function() { describe('.id()', function() { @@ -186,11 +180,11 @@ describe('Message model', function() { }); }); - describe('mixins inheritance', function() { - assertBindingsMixinInheritance(MessageTrait); - assertDescriptionMixinInheritance(MessageTrait); - assertExtensionsMixinInheritance(MessageTrait); - assertExternalDocumentationMixinInheritance(MessageTrait); - assertTagsMixinInheritance(MessageTrait); + describe('mixins', function() { + assertBindings(MessageTrait); + assertDescription(MessageTrait); + assertExtensions(MessageTrait); + assertExternalDocumentation(MessageTrait); + assertTags(MessageTrait); }); }); diff --git a/test/models/v2/mixins.spec.ts b/test/models/v2/mixins.spec.ts new file mode 100644 index 000000000..b7fa77736 --- /dev/null +++ b/test/models/v2/mixins.spec.ts @@ -0,0 +1,150 @@ +import { BaseModel } from '../../../src/models/base'; +import { bindings, hasDescription, description, extensions, hasExternalDocs, externalDocs, tags } from '../../../src/models/v2/mixins'; +import { BindingsV2, ExtensionsV2, ExternalDocumentationV2, TagsV2 } from '../../../src/models/v2'; + + +describe('mixins', function() { + describe('bindings', function() { + class Model extends BaseModel {}; + + const doc1 = { bindings: { amqp: { test: 'test1' } } }; + const doc2 = { bindings: {} }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a collection of bindings', function() { + expect(bindings(d1)).toBeInstanceOf(BindingsV2); + expect(bindings(d1).length).toEqual(1); + }); + + it('should return an empty object', function() { + expect(bindings(d2)).toBeInstanceOf(BindingsV2); + expect(bindings(d2).length).toEqual(0); + expect(bindings(d3)).toBeInstanceOf(BindingsV2); + expect(bindings(d3).length).toEqual(0); + }); + }); + + describe('hasDescription', function() { + class Model extends BaseModel {}; + + const doc1 = { description: 'Testing' }; + const doc2 = { description: '' }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a boolean indicating if the object has description', function() { + expect(hasDescription(d1)).toEqual(true); + expect(hasDescription(d2)).toEqual(false); + expect(hasDescription(d3)).toEqual(false); + }); + }); + + describe('description', function() { + class Model extends BaseModel {}; + + const doc1 = { description: 'Testing' }; + const doc2 = { description: '' }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a value', function() { + expect(description(d1)).toEqual(doc1.description); + expect(description(d2)).toEqual(''); + }); + + it('should return an undefined', function() { + expect(description(d3)).toEqual(undefined); + }); + }); + + describe('extensions', function() { + class Model extends BaseModel {}; + + const doc1 = { 'x-test': 'testing', test: 'testing' }; + const doc2 = { test: 'testing' }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a collection with extensions', function() { + expect(extensions(d1)).toBeInstanceOf(ExtensionsV2); + expect(extensions(d1).length).toEqual(1); + }); + + it('should return a empty object', function() { + expect(extensions(d2)).toBeInstanceOf(ExtensionsV2); + expect(extensions(d2).length).toEqual(0); + expect(extensions(d3)).toBeInstanceOf(ExtensionsV2); + expect(extensions(d3).length).toEqual(0); + }); + }); + + describe('hasExternalDocs', function() { + class Model extends BaseModel {}; + + const doc1 = { externalDocs: { url: 'test.com' } }; + const doc2 = { externalDocs: {} }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a boolean indicating if the object has externalDocs', function() { + expect(hasExternalDocs(d1)).toEqual(true); + expect(hasExternalDocs(d2)).toEqual(false); + expect(hasExternalDocs(d3)).toEqual(false); + }); + }); + + describe('externalDocs', function() { + class Model extends BaseModel {}; + + const doc1 = { externalDocs: { url: 'test.com' } }; + const doc2 = { externalDocs: {} }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return a externalDocs object', function() { + expect(externalDocs(d1)).toBeInstanceOf(ExternalDocumentationV2); + expect(externalDocs(d1)!.json()).toEqual(doc1.externalDocs); + }); + + it('should return a undefined', function() { + expect(externalDocs(d2)).toEqual(undefined); + expect(externalDocs(d3)).toEqual(undefined); + }); + }); + + describe('tags', function() { + class Model extends BaseModel {}; + + const doc1 = { tags: [{ name: 'test1' }, { name: 'test2' }] }; + const doc2 = { tags: [] }; + const doc3 = {}; + const d1 = new Model(doc1); + const d2 = new Model(doc2); + const d3 = new Model(doc3); + + it('should return an array of tag objects', function() { + expect(tags(d1)).toBeInstanceOf(TagsV2); + expect(tags(d1).length).toEqual(2); + }); + + it('should return an empty array', function() { + expect(tags(d2)).toBeInstanceOf(TagsV2); + expect(tags(d2).length).toEqual(0); + expect(tags(d3)).toBeInstanceOf(TagsV2); + expect(tags(d3).length).toEqual(0); + }); + }); +}); diff --git a/test/models/v2/mixins/bindings.spec.ts b/test/models/v2/mixins/bindings.spec.ts deleted file mode 100644 index cb85bf53d..000000000 --- a/test/models/v2/mixins/bindings.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseModel } from '../../../../src/models/base'; -import { Mixin } from '../../../../src/models/utils'; -import { BindingsMixin } from '../../../../src/models/v2/mixins/bindings'; - -describe('Bindings mixin', function() { - class Model extends Mixin(BaseModel, BindingsMixin) {}; - - const doc1 = { bindings: { amqp: { test: 'test1' } } }; - const doc2 = { bindings: {} }; - const doc3 = {}; - const d1 = new Model(doc1); - const d2 = new Model(doc2); - const d3 = new Model(doc3); - - describe('.bindings()', function() { - it('should return a collection of bindings', function() { - expect(d1.bindings().length).toEqual(1); - }); - - it('should return an empty object', function() { - expect(d2.bindings().length).toEqual(0); - expect(d3.bindings().length).toEqual(0); - }); - }); -}); \ No newline at end of file diff --git a/test/models/v2/mixins/description.spec.ts b/test/models/v2/mixins/description.spec.ts deleted file mode 100644 index 32ba29999..000000000 --- a/test/models/v2/mixins/description.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { BaseModel } from '../../../../src/models/base'; -import { Mixin } from '../../../../src/models/utils'; -import { DescriptionMixin } from '../../../../src/models/v2/mixins/description'; - -describe('Description mixin', function() { - class Model extends Mixin(BaseModel, DescriptionMixin) {}; - - const doc1 = { description: 'Testing' }; - const doc2 = { description: '' }; - const doc3 = {}; - const d1 = new Model(doc1); - const d2 = new Model(doc2); - const d3 = new Model(doc3); - - describe('.hasDescription()', function() { - it('should return a boolean indicating if the object has description', function() { - expect(d1.hasDescription()).toEqual(true); - expect(d2.hasDescription()).toEqual(false); - expect(d3.hasDescription()).toEqual(false); - }); - }); - - describe('.description()', function() { - it('should return a value', function() { - expect(d1.description()).toEqual(doc1.description); - expect(d2.description()).toEqual(''); - }); - - it('should return an undefined', function() { - expect(d3.description()).toEqual(undefined); - }); - }); -}); \ No newline at end of file diff --git a/test/models/v2/mixins/extensions.spec.ts b/test/models/v2/mixins/extensions.spec.ts deleted file mode 100644 index 51e28ed15..000000000 --- a/test/models/v2/mixins/extensions.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseModel } from '../../../../src/models/base'; -import { Mixin } from '../../../../src/models/utils'; -import { ExtensionsMixin } from '../../../../src/models/v2/mixins/extensions'; - -describe('Extensions mixin', function() { - class Model extends Mixin(BaseModel, ExtensionsMixin) {}; - - const doc1 = { 'x-test': 'testing', test: 'testing' }; - const doc2 = { test: 'testing' }; - const doc3 = {}; - const d1 = new Model(doc1); - const d2 = new Model(doc2); - const d3 = new Model(doc3); - - describe('.extensions()', function() { - it('should return a collection with extensions', function() { - expect(d1.extensions().length).toEqual(1); - }); - - it('should return a empty object', function() { - expect(d2.extensions().length).toEqual(0); - expect(d3.extensions().length).toEqual(0); - }); - }); -}); \ No newline at end of file diff --git a/test/models/v2/mixins/external-docs.spec.ts b/test/models/v2/mixins/external-docs.spec.ts deleted file mode 100644 index f9aac7bc9..000000000 --- a/test/models/v2/mixins/external-docs.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BaseModel } from '../../../../src/models/base'; -import { Mixin } from '../../../../src/models/utils'; -import { ExternalDocumentationV2 } from '../../../../src/models/v2'; -import { ExternalDocumentationMixin } from '../../../../src/models/v2/mixins/external-docs'; - -describe('ExternalDocs mixin', function() { - class Model extends Mixin(BaseModel, ExternalDocumentationMixin) {}; - - const doc1 = { externalDocs: { url: 'test.com' } }; - const doc2 = { externalDocs: {} }; - const doc3 = {}; - const d1 = new Model(doc1); - const d2 = new Model(doc2); - const d3 = new Model(doc3); - - describe('.hasExternalDocs()', function() { - it('should return a boolean indicating if the object has externalDocs', function() { - expect(d1.hasExternalDocs()).toEqual(true); - expect(d2.hasExternalDocs()).toEqual(false); - expect(d3.hasExternalDocs()).toEqual(false); - }); - }); - - describe('.externalDocs()', function() { - it('should return a externalDocs object', function() { - expect(d1.externalDocs()).toBeInstanceOf(ExternalDocumentationV2); - expect(d1.externalDocs()!.json()).toEqual(doc1.externalDocs); - }); - - it('should return a undefined', function() { - expect(d2.externalDocs()).toEqual(undefined); - expect(d3.externalDocs()).toEqual(undefined); - }); - }); -}); \ No newline at end of file diff --git a/test/models/v2/mixins/inheritance.ts b/test/models/v2/mixins/inheritance.ts deleted file mode 100644 index f0fa18333..000000000 --- a/test/models/v2/mixins/inheritance.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { BindingsMixin } from '../../../../src/models/v2/mixins/bindings'; -import { DescriptionMixin } from '../../../../src/models/v2/mixins/description'; -import { ExtensionsMixin } from '../../../../src/models/v2/mixins/extensions'; -import { ExternalDocumentationMixin } from '../../../../src/models/v2/mixins/external-docs'; -import { TagsMixin } from '../../../../src/models/v2/mixins/tags'; - -import type { Constructor } from '../../../../src/models/utils'; - -export function assertBindingsMixinInheritance(model: Constructor) { - describe('BindingsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from BindingsMixin`, function() { - expect(model.prototype.bindings).not.toEqual(undefined); - expect(typeof model.prototype.bindings).toEqual('function'); - expect(model.prototype.bindings === BindingsMixin.prototype.bindings).toEqual(true); - }); - }); -} - -export function assertDescriptionMixinInheritance(model: Constructor) { - describe('DescriptionMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from DescriptionMixin`, function() { - expect(model.prototype.hasDescription).not.toEqual(undefined); - expect(typeof model.prototype.hasDescription).toEqual('function'); - expect(model.prototype.hasDescription === DescriptionMixin.prototype.hasDescription).toEqual(true); - - expect(model.prototype.description).not.toEqual(undefined); - expect(typeof model.prototype.description).toEqual('function'); - expect(model.prototype.description === DescriptionMixin.prototype.description).toEqual(true); - }); - }); -} - -export function assertExtensionsMixinInheritance(model: Constructor) { - describe('SpecificationExtensionsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from ExtensionsMixin`, function() { - expect(model.prototype.extensions).not.toEqual(undefined); - expect(typeof model.prototype.extensions).toEqual('function'); - expect(model.prototype.extensions === ExtensionsMixin.prototype.extensions).toEqual(true); - }); - }); -} - -export function assertExternalDocumentationMixinInheritance(model: Constructor) { - describe('ExternalDocsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from ExternalDocumentationMixin`, function() { - expect(model.prototype.hasExternalDocs).not.toEqual(undefined); - expect(typeof model.prototype.hasExternalDocs).toEqual('function'); - expect(model.prototype.hasExternalDocs === ExternalDocumentationMixin.prototype.hasExternalDocs).toEqual(true); - - expect(model.prototype.externalDocs).not.toEqual(undefined); - expect(typeof model.prototype.externalDocs).toEqual('function'); - expect(model.prototype.externalDocs === ExternalDocumentationMixin.prototype.externalDocs).toEqual(true); - }); - }); -} - -export function assertTagsMixinInheritance(model: Constructor) { - describe('TagsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from TagsMixin`, function() { - expect(model.prototype.tags).not.toEqual(undefined); - expect(typeof model.prototype.tags).toEqual('function'); - expect(model.prototype.tags === TagsMixin.prototype.tags).toEqual(true); - }); - }); -} \ No newline at end of file diff --git a/test/models/v2/mixins/tags.spec.ts b/test/models/v2/mixins/tags.spec.ts deleted file mode 100644 index 29a658254..000000000 --- a/test/models/v2/mixins/tags.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BaseModel } from '../../../../src/models/base'; -import { Mixin } from '../../../../src/models/utils'; -import { TagsMixin } from '../../../../src/models/v2/mixins/tags'; - -describe('Tags mixin', function() { - class Model extends Mixin(BaseModel, TagsMixin) {}; - - const doc1 = { tags: [{ name: 'test1' }, { name: 'test2' }] }; - const doc2 = { tags: [] }; - const doc3 = {}; - const d1 = new Model(doc1); - const d2 = new Model(doc2); - const d3 = new Model(doc3); - - describe('#tags()', function() { - it('should return an array of tag objects', function() { - expect(d1.tags().length).toEqual(2); - }); - - it('should return an empty array', function() { - expect(d2.tags().length).toEqual(0); - expect(d3.tags().length).toEqual(0); - }); - }); -}); \ No newline at end of file diff --git a/test/models/v2/oauth-flow.spec.ts b/test/models/v2/oauth-flow.spec.ts index 8435af1cb..a7d1ef339 100644 --- a/test/models/v2/oauth-flow.spec.ts +++ b/test/models/v2/oauth-flow.spec.ts @@ -1,5 +1,7 @@ import { OAuthFlow } from '../../../src/models/v2/oauth-flow'; +import type { v2 } from '../../../src/spec-types'; + const flowObject = { "authorizationUrl": "https://example.com/api/oauth/dialog", "scopes": { @@ -9,7 +11,7 @@ const flowObject = { } const flow = new OAuthFlow(flowObject); -const emptyObject = new OAuthFlow({}); +const emptyObject = new OAuthFlow({} as v2.OAuthFlowObject); describe('OAuth Flow', function(){ describe('.authorizationUrl()', function(){ diff --git a/test/models/v2/operation-trait.spec.ts b/test/models/v2/operation-trait.spec.ts index b30e1ebe1..9abfb7a9c 100644 --- a/test/models/v2/operation-trait.spec.ts +++ b/test/models/v2/operation-trait.spec.ts @@ -1,13 +1,7 @@ import { OperationTrait } from '../../../src/models/v2/operation-trait'; import { SecurityScheme } from '../../../src/models/v2/security-scheme'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance, - assertTagsMixinInheritance, -} from './mixins/inheritance'; +import { assertBindings, assertDescription, assertExtensions, assertExternalDocumentation, assertTags } from './utils'; describe('OperationTrait model', function() { describe('.id()', function() { @@ -107,11 +101,11 @@ describe('OperationTrait model', function() { }); }); - describe('mixins inheritance', function() { - assertBindingsMixinInheritance(OperationTrait); - assertDescriptionMixinInheritance(OperationTrait); - assertExtensionsMixinInheritance(OperationTrait); - assertExternalDocumentationMixinInheritance(OperationTrait); - assertTagsMixinInheritance(OperationTrait); + describe('mixins', function() { + assertBindings(OperationTrait); + assertDescription(OperationTrait); + assertExtensions(OperationTrait); + assertExternalDocumentation(OperationTrait); + assertTags(OperationTrait); }); }); diff --git a/test/models/v2/operation.spec.ts b/test/models/v2/operation.spec.ts index 128bd9203..8e9303530 100644 --- a/test/models/v2/operation.spec.ts +++ b/test/models/v2/operation.spec.ts @@ -8,13 +8,7 @@ import { Message } from '../../../src/models/v2/message'; import { Servers } from '../../../src/models/v2/servers'; import { Server } from '../../../src/models/v2/server'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance, - assertTagsMixinInheritance, -} from './mixins/inheritance'; +import { assertBindings, assertDescription, assertExtensions, assertExternalDocumentation, assertTags } from './utils'; describe('Operation model', function() { describe('.id()', function() { @@ -138,11 +132,11 @@ describe('Operation model', function() { }); }); - describe('mixins inheritance', function() { - assertBindingsMixinInheritance(Operation); - assertDescriptionMixinInheritance(Operation); - assertExtensionsMixinInheritance(Operation); - assertExternalDocumentationMixinInheritance(Operation); - assertTagsMixinInheritance(Operation); + describe('mixins', function() { + assertBindings(Operation); + assertDescription(Operation); + assertExtensions(Operation); + assertExternalDocumentation(Operation); + assertTags(Operation); }); }); diff --git a/test/models/v2/schema.spec.ts b/test/models/v2/schema.spec.ts index 8444c5490..cf1eac221 100644 --- a/test/models/v2/schema.spec.ts +++ b/test/models/v2/schema.spec.ts @@ -1,9 +1,8 @@ import { Schema } from '../../../src/models/v2/schema'; -import { - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance, -} from './mixins/inheritance'; +import { assertExtensions, assertExternalDocumentation } from './utils'; + +import type { v2 } from '../../../src/spec-types'; describe('Channel model', function() { describe('.id()', function() { @@ -418,7 +417,7 @@ describe('Channel model', function() { describe('.isCircular()', function() { it('should return a true when schema has circular reference', function() { - const doc = { + const doc: v2.AsyncAPISchemaObject = { properties: { nonCircular: { type: 'string', @@ -426,7 +425,7 @@ describe('Channel model', function() { circular: {}, } }; - doc.properties.circular = doc; + doc.properties!.circular = doc; const d = new Schema(doc); expect(d.isCircular()).toEqual(false); expect((d.properties() as any)['nonCircular'].isCircular()).toEqual(false); @@ -750,13 +749,13 @@ describe('Channel model', function() { describe('.type()', function() { it('should return single type', function() { - const doc = { type: 'object' }; + const doc: v2.AsyncAPISchemaObject = { type: 'object' }; const d = new Schema(doc); expect(d.type()).toEqual(doc.type); }); it('should return array of type', function() { - const doc = { type: ['object', 'array'] }; + const doc: v2.AsyncAPISchemaObject = { type: ['object', 'array'] }; const d = new Schema(doc); expect(d.type()).toEqual(doc.type); }); @@ -796,8 +795,8 @@ describe('Channel model', function() { }); }); - describe('mixins inheritance', function() { - assertExtensionsMixinInheritance(Schema); - assertExternalDocumentationMixinInheritance(Schema); + describe('mixins', function() { + assertExtensions(Schema); + assertExternalDocumentation(Schema); }); }); diff --git a/test/models/v2/security-scheme.spec.ts b/test/models/v2/security-scheme.spec.ts index 9ccf6c230..887cc70e9 100644 --- a/test/models/v2/security-scheme.spec.ts +++ b/test/models/v2/security-scheme.spec.ts @@ -1,7 +1,9 @@ import { SecurityScheme } from '../../../src/models/v2/security-scheme'; -import {OAuthFlows} from '../../../src/models/v2/oauth-flows'; +import { OAuthFlows } from '../../../src/models/v2/oauth-flows'; -const doc1 = { +import type { v2 } from '../../../src/spec-types'; + +const doc1: v2.SecuritySchemeObject = { type: 'http', in: 'header', scheme: 'bearer', @@ -19,7 +21,7 @@ const doc1 = { } const sc1 = new SecurityScheme(doc1, { asyncapi: {} as any, pointer: '', id: 'api_key' }); -const emptyItem = new SecurityScheme({}); +const emptyItem = new SecurityScheme({ type: 'X509' }); describe('Security Scheme', function () { describe('.id()', function () { @@ -46,14 +48,14 @@ describe('Security Scheme', function () { describe('.in()', function () { it('should return in if present', function () { - expect(sc1.in()).toMatch(doc1.in); + expect(sc1.in()).toMatch(doc1.in as string); expect(emptyItem.in()).toBeUndefined(); }) }) describe('.openIdConnectUrl()', function () { it('should return openIdConnectUrl value', function () { - expect(sc1.openIdConnectUrl()).toMatch(doc1.openIdConnectUrl); + expect(sc1.openIdConnectUrl()).toMatch(doc1.openIdConnectUrl as string); }) }) describe('.flows()', function () { diff --git a/test/models/v2/server.spec.ts b/test/models/v2/server.spec.ts index 68c3681a4..514166022 100644 --- a/test/models/v2/server.spec.ts +++ b/test/models/v2/server.spec.ts @@ -8,11 +8,9 @@ import { Server } from '../../../src/models/v2/server'; import { ServerVariables } from '../../../src/models/v2/server-variables'; import { SecurityScheme } from '../../../src/models/v2/security-scheme'; -import { - assertBindingsMixinInheritance, - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, -} from './mixins/inheritance'; +import { serializeInput, assertBindings, assertDescription, assertExtensions } from './utils'; + +import type { v2 } from '../../../src/spec-types'; const doc = { 'development': { @@ -28,7 +26,7 @@ const doc = { } }; const docItem = new Server(doc.development, { asyncapi: {} as any, pointer: '', id: 'development' }); -const emptyItem = new Server({}, { asyncapi: {} as any, pointer: '', id: '' }); +const emptyItem = new Server(serializeInput({}), { asyncapi: {} as any, pointer: '', id: '' }); describe('Server Model', function () { describe('.id()', function () { @@ -71,7 +69,7 @@ describe('Server Model', function () { describe('.channels()', function() { it('should return collection of channels - single channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': {} } } } as any, pointer: '', id: 'production' }); expect(d.channels()).toBeInstanceOf(Channels); expect(d.channels().all()).toHaveLength(1); @@ -80,7 +78,7 @@ describe('Server Model', function () { }); it('should return collection of channels - multiple channels', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': {}, 'user/logout': {} } } } as any, pointer: '', id: 'production' }); expect(d.channels()).toBeInstanceOf(Channels); expect(d.channels().all()).toHaveLength(2); @@ -91,7 +89,7 @@ describe('Server Model', function () { }); it('should return collection of channels - server available only in particular channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { servers: ['production'] }, 'user/logout': { servers: ['development'] }, 'user/create': {} } } } as any, pointer: '', id: 'production', }); expect(d.channels()).toBeInstanceOf(Channels); expect(d.channels().all()).toHaveLength(2); @@ -104,7 +102,7 @@ describe('Server Model', function () { describe('.operations()', function() { it('should return collection of operations - single channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { operationId: '1' } } } } } as any, pointer: '', id: 'production' }); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(1); @@ -113,7 +111,7 @@ describe('Server Model', function () { }); it('should return collection of channels - multiple channels', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { operationId: '1' } }, 'user/logout': { subscribe: { operationId: '2' } } } } } as any, pointer: '', id: 'production' }); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(2); @@ -124,7 +122,7 @@ describe('Server Model', function () { }); it('should return collection of channels - server available only in particular channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { servers: ['production'], publish: { operationId: '1' } }, 'user/logout': { servers: ['development'] }, 'user/create': { subscribe: { operationId: '3' }, publish: { operationId: '2' } } } } } as any, pointer: '', id: 'production', }); expect(d.operations()).toBeInstanceOf(Operations); expect(d.operations().all()).toHaveLength(3); @@ -139,7 +137,7 @@ describe('Server Model', function () { describe('.messages()', function() { it('should return collection of messages - single channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: { messageId: '1' } } } } } } as any, pointer: '', id: 'production' }); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(1); @@ -148,7 +146,7 @@ describe('Server Model', function () { }); it('should return collection of messages - multiple channels', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { publish: { message: { messageId: '1' } } }, 'user/logout': { subscribe: { message: { oneOf: [{ messageId: '2' }, { messageId: '3' }] } } } } } } as any, pointer: '', id: 'production' }); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(3); @@ -161,7 +159,7 @@ describe('Server Model', function () { }); it('should return collection of messages - server available only in particular channel', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc, { asyncapi: { parsed: { channels: { 'user/signup': { servers: ['production'], publish: { message: { messageId: '1' } } }, 'user/logout': { servers: ['development'] }, 'user/create': { subscribe: { message: { messageId: '3' } }, publish: { message: { messageId: '2' } } } } } } as any, pointer: '', id: 'production', }); expect(d.messages()).toBeInstanceOf(Messages); expect(d.messages().all()).toHaveLength(3); @@ -182,7 +180,7 @@ describe('Server Model', function () { describe('.security()', function() { it('should return collection of security requirements', function() { - const doc = { security: [ { requirement: [] } ] }; + const doc = serializeInput({ security: [ { requirement: [] } ] }); const d = new Server(doc); expect(Array.isArray(d.security())).toEqual(true); expect(d.security()).toHaveLength(1); @@ -192,7 +190,7 @@ describe('Server Model', function () { }); it('should return collection of security requirements when value is undefined', function() { - const doc = {}; + const doc = serializeInput({}); const d = new Server(doc); expect(Array.isArray(d.security())).toEqual(true); expect(d.security()).toHaveLength(0); @@ -200,8 +198,8 @@ describe('Server Model', function () { }); describe('mixins inheritance', function () { - assertBindingsMixinInheritance(Server); - assertDescriptionMixinInheritance(Server); - assertExtensionsMixinInheritance(Server); + assertBindings(Server); + assertDescription(Server); + assertExtensions(Server); }); }) diff --git a/test/models/v2/tag.spec.ts b/test/models/v2/tag.spec.ts index 8bda373bc..344f23369 100644 --- a/test/models/v2/tag.spec.ts +++ b/test/models/v2/tag.spec.ts @@ -1,10 +1,6 @@ -import { Tag } from '../../../src/models/v2/mixins/tags'; +import { Tag } from '../../../src/models/v2/tag'; -import { - assertDescriptionMixinInheritance, - assertExtensionsMixinInheritance, - assertExternalDocumentationMixinInheritance -} from './mixins/inheritance'; +import { assertDescription, assertExtensions, assertExternalDocumentation } from './utils'; describe('Tag model', function() { describe('.name()', function() { @@ -16,8 +12,8 @@ describe('Tag model', function() { }); describe('mixins inheritance', function() { - assertDescriptionMixinInheritance(Tag); - assertExtensionsMixinInheritance(Tag); - assertExternalDocumentationMixinInheritance(Tag); + assertDescription(Tag); + assertExtensions(Tag); + assertExternalDocumentation(Tag); }); }); diff --git a/test/models/v2/tags.spec.ts b/test/models/v2/tags.spec.ts index b6b65807e..eb09f6edb 100644 --- a/test/models/v2/tags.spec.ts +++ b/test/models/v2/tags.spec.ts @@ -1,4 +1,5 @@ -import { Tags, Tag } from '../../../src/models/v2/mixins/tags'; +import { Tags } from '../../../src/models/v2/tags'; +import { Tag } from '../../../src/models/v2/tag'; const tag = { name: 'test', diff --git a/test/models/v2/utils.ts b/test/models/v2/utils.ts new file mode 100644 index 000000000..2ed63ef71 --- /dev/null +++ b/test/models/v2/utils.ts @@ -0,0 +1,150 @@ +import { BindingsV2, ExtensionsV2, ExternalDocumentationV2, TagsV2 } from '../../../src/models/v2'; + +import type { Constructor } from '../../../src/models/utils'; +import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface } from '../../../src/models/mixins'; + +type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial } : T; + +export function serializeInput(data: DeepPartial): T { + return data as T; +} + +export function assertBindings(model: Constructor) { + describe('.bindings()', function() { + const doc1 = { bindings: { amqp: { test: 'test1' } } }; + const doc2 = { bindings: {} }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a collection of bindings', function() { + expect(d1.bindings()).toBeInstanceOf(BindingsV2); + expect(d1.bindings().length).toEqual(1); + }); + + it('should return an empty object', function() { + expect(d2.bindings()).toBeInstanceOf(BindingsV2); + expect(d2.bindings().length).toEqual(0); + expect(d3.bindings()).toBeInstanceOf(BindingsV2); + expect(d3.bindings().length).toEqual(0); + }); + }); +} + +export function assertDescription(model: Constructor) { + describe('.hasDescription()', function() { + const doc1 = { description: 'Testing' }; + const doc2 = { description: '' }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a boolean indicating if the object has description', function() { + expect(d1.hasDescription()).toEqual(true); + expect(d2.hasDescription()).toEqual(false); + expect(d3.hasDescription()).toEqual(false); + }); + }); + + describe('.description()', function() { + const doc1 = { description: 'Testing' }; + const doc2 = { description: '' }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a value', function() { + expect(d1.description()).toEqual(doc1.description); + expect(d2.description()).toEqual(''); + }); + + it('should return an undefined', function() { + expect(d3.description()).toEqual(undefined); + }); + }); +} + +export function assertExtensions(model: Constructor) { + describe('.extensions()', function() { + const doc1 = { 'x-test': 'testing', test: 'testing' }; + const doc2 = { test: 'testing' }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a collection with extensions', function() { + expect(d1.extensions()).toBeInstanceOf(ExtensionsV2); + expect(d1.extensions().length).toEqual(1); + }); + + it('should return a empty object', function() { + expect(d2.extensions()).toBeInstanceOf(ExtensionsV2); + expect(d2.extensions().length).toEqual(0); + expect(d3.extensions()).toBeInstanceOf(ExtensionsV2); + expect(d3.extensions().length).toEqual(0); + }); + }); +} + +export function assertExternalDocumentation(model: Constructor) { + describe('.hasExternalDocs()', function() { + const doc1 = { externalDocs: { url: 'test.com' } }; + const doc2 = { externalDocs: {} }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a boolean indicating if the object has externalDocs', function() { + expect(d1.hasExternalDocs()).toEqual(true); + expect(d2.hasExternalDocs()).toEqual(false); + expect(d3.hasExternalDocs()).toEqual(false); + }); + }); + + describe('.externalDocs()', function() { + const doc1 = { externalDocs: { url: 'test.com' } }; + const doc2 = { externalDocs: {} }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return a externalDocs object', function() { + expect(d1.externalDocs()).toBeInstanceOf(ExternalDocumentationV2); + expect(d1.externalDocs()!.json()).toEqual(doc1.externalDocs); + }); + + it('should return a undefined', function() { + expect(d2.externalDocs()).toEqual(undefined); + expect(d3.externalDocs()).toEqual(undefined); + }); + }); +} + +export function assertTags(model: Constructor) { + describe('tags', function() { + const doc1 = { tags: [{ name: 'test1' }, { name: 'test2' }] }; + const doc2 = { tags: [] }; + const doc3 = {}; + const d1 = new model(doc1); + const d2 = new model(doc2); + const d3 = new model(doc3); + + it('should return an array of tag objects', function() { + expect(d1.tags()).toBeInstanceOf(TagsV2); + expect(d1.tags().length).toEqual(2); + }); + + it('should return an empty array', function() { + expect(d2.tags()).toBeInstanceOf(TagsV2); + expect(d2.tags().length).toEqual(0); + expect(d3.tags()).toBeInstanceOf(TagsV2); + expect(d3.tags().length).toEqual(0); + }); + }); +} \ No newline at end of file diff --git a/test/schema-parser/avro/avro-schema-parser.spec.ts b/test/schema-parser/avro/avro-schema-parser.spec.ts index 5d1b48257..f9c26015b 100644 --- a/test/schema-parser/avro/avro-schema-parser.spec.ts +++ b/test/schema-parser/avro/avro-schema-parser.spec.ts @@ -259,7 +259,7 @@ function toParseInput(raw: string): ParseSchemaInput { patch: 0 }, source: "", - parsed: {}, + parsed: {} as any, }, data: message.payload, meta: { diff --git a/test/schema-parser/openapi/openapi-schema-parser.spec.ts b/test/schema-parser/openapi/openapi-schema-parser.spec.ts index 62101227f..d1f1fa1a2 100644 --- a/test/schema-parser/openapi/openapi-schema-parser.spec.ts +++ b/test/schema-parser/openapi/openapi-schema-parser.spec.ts @@ -76,7 +76,7 @@ function toParseInput(raw: string): ParseSchemaInput | ValidateSchemaInput { patch: 0 }, source: "", - parsed: {}, + parsed: {} as any, }, data: message.payload, meta: { diff --git a/test/schema-parser/raml/raml-schema-parser.spec.ts b/test/schema-parser/raml/raml-schema-parser.spec.ts index 9479129a2..df3acbb9a 100644 --- a/test/schema-parser/raml/raml-schema-parser.spec.ts +++ b/test/schema-parser/raml/raml-schema-parser.spec.ts @@ -61,7 +61,7 @@ function toInput(raw: string): ParseSchemaInput | ValidateSchemaInput { patch: 0 }, source: "", - parsed: {}, + parsed: {} as any, }, path: ["otherchannel", "subscribe", "message", "payload"], data: message.payload, diff --git a/test/stringify.spec.ts b/test/stringify.spec.ts index 14c79eee9..ac052053d 100644 --- a/test/stringify.spec.ts +++ b/test/stringify.spec.ts @@ -33,7 +33,7 @@ describe('stringify & unstringify', function() { it('should stringify AsyncAPIDocument instance', function() { const doc = { asyncapi: '2.0.0' }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(typeof stringify(newAsyncAPIDocument(detailed))).toEqual('string'); }); }); diff --git a/test/utils.spec.ts b/test/utils.spec.ts index e5ebfdca3..7e5cbf5be 100644 --- a/test/utils.spec.ts +++ b/test/utils.spec.ts @@ -41,7 +41,7 @@ describe('utils', function() { it('AsyncAPIDocument instance should return AsyncAPIDocument instance', function() { const doc = { asyncapi: '2.0.0' }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(toAsyncAPIDocument(newAsyncAPIDocument(detailed))).toBeInstanceOf(AsyncAPIDocumentV2); }); @@ -77,7 +77,7 @@ describe('utils', function() { it('AsyncAPIDocument instance should be AsyncAPI document', function() { const doc = { asyncapi: '2.0.0' }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(isAsyncAPIDocument(newAsyncAPIDocument(detailed))).toEqual(true); }); }); @@ -101,13 +101,13 @@ describe('utils', function() { it('AsyncAPIDocument instance should not be parsed document', function() { const doc = { asyncapi: '2.0.0' }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(isParsedDocument(newAsyncAPIDocument(detailed))).toEqual(false); }); it('AsyncAPIDocument instance with proper extension should not be parsed document', function() { const doc = { asyncapi: '2.0.0', [xParserSpecParsed]: true }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(isParsedDocument(newAsyncAPIDocument(detailed))).toEqual(false); }); @@ -135,13 +135,13 @@ describe('utils', function() { it('AsyncAPIDocument instance should not be parsed document', function() { const doc = { asyncapi: '2.0.0' }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(isStringifiedDocument(newAsyncAPIDocument(detailed))).toEqual(false); }); it('AsyncAPIDocument instance with proper extension should not be parsed document', function() { const doc = { asyncapi: '2.0.0', [xParserSpecParsed]: true, [xParserSpecStringified]: true }; - const detailed = createDetailedAsyncAPI(doc, doc); + const detailed = createDetailedAsyncAPI(doc, doc as any); expect(isStringifiedDocument(newAsyncAPIDocument(detailed))).toEqual(false); }); @@ -238,7 +238,7 @@ describe('utils', function() { it('should create detailed object', function () { const source = "{ asyncapi: '2.1.37' }"; const parsed = { asyncapi: '2.1.37' }; - const detailed = createDetailedAsyncAPI(source, parsed); + const detailed = createDetailedAsyncAPI(source, parsed as any); expect(detailed.source).toEqual(source); expect(detailed.parsed).toEqual(parsed); expect(detailed.semver.version).toEqual('2.1.37');