diff --git a/src/models/asyncapi.ts b/src/models/asyncapi.ts index e5f95f030..6fb68a1ab 100644 --- a/src/models/asyncapi.ts +++ b/src/models/asyncapi.ts @@ -1,12 +1,11 @@ -import { InfoInterface } from "./info"; -import { BaseModel } from "./base"; - import { AsyncAPIDocumentV2 } from "./v2"; import { AsyncAPIDocumentV3 } from "./v3"; -import { ExternalDocsMixinInterface, SpecificationExtensionsMixinInterface, TagsMixinInterface } from "./mixins"; +import type { InfoInterface } from "./info"; +import type { BaseModel } from "./base"; +import type { ExtensionsMixinInterface } from "./mixins"; -export interface AsyncAPIDocumentInterface extends BaseModel, ExternalDocsMixinInterface, SpecificationExtensionsMixinInterface, TagsMixinInterface { +export interface AsyncAPIDocumentInterface extends BaseModel, ExtensionsMixinInterface { version(): string; info(): InfoInterface; } diff --git a/src/models/binding.ts b/src/models/binding.ts new file mode 100644 index 000000000..298c1eeb7 --- /dev/null +++ b/src/models/binding.ts @@ -0,0 +1,8 @@ +import type { BaseModel } from "./base"; +import type { ExtensionsMixinInterface } from './mixins'; + +export interface BindingInterface extends BaseModel, ExtensionsMixinInterface { + protocol(): string; + version(): string; + value(): any; +} \ No newline at end of file diff --git a/src/models/bindings.ts b/src/models/bindings.ts new file mode 100644 index 000000000..9902a9c78 --- /dev/null +++ b/src/models/bindings.ts @@ -0,0 +1,4 @@ +import type { Collection } from './collection'; +import type { BindingInterface } from './binding'; + +export interface BindingsInterface extends Collection {} \ No newline at end of file diff --git a/src/models/collection.ts b/src/models/collection.ts new file mode 100644 index 000000000..9e8c89604 --- /dev/null +++ b/src/models/collection.ts @@ -0,0 +1,20 @@ +import type { BaseModel } from "./base"; + +export abstract class Collection extends Array { + constructor( + protected readonly collections: T[] + ) { + super(...collections); + } + + abstract get(id: string): T | undefined; + abstract has(id: string): boolean; + + all(): T[] { + return this.collections; + } + + isEmpty(): boolean { + return this.collections.length === 0; + } +} \ No newline at end of file diff --git a/src/models/contact.ts b/src/models/contact.ts index 7564d384d..709299095 100644 --- a/src/models/contact.ts +++ b/src/models/contact.ts @@ -1,7 +1,12 @@ -import { BaseModel } from "./base"; +import type { BaseModel } from "./base"; -export interface ContactInterface extends BaseModel { - name(): string; - url(): string; - email(): string; +import type { ExtensionsMixinInterface } from "./mixins"; + +export interface ContactInterface extends BaseModel, ExtensionsMixinInterface { + hasName(): boolean; + name(): string | undefined; + hasUrl(): boolean; + url(): string | undefined; + hasEmail(): boolean; + email(): string | undefined; } \ No newline at end of file diff --git a/src/models/extension.ts b/src/models/extension.ts new file mode 100644 index 000000000..a21d16024 --- /dev/null +++ b/src/models/extension.ts @@ -0,0 +1,7 @@ +import type { BaseModel } from "./base"; + +export interface ExtensionInterface extends BaseModel { + id(): string; + version(): string; + value(): any; +} diff --git a/src/models/extensions.ts b/src/models/extensions.ts new file mode 100644 index 000000000..a1693ec6c --- /dev/null +++ b/src/models/extensions.ts @@ -0,0 +1,4 @@ +import type { Collection } from './collection'; +import type { ExtensionInterface } from './extension'; + +export interface ExtensionsInterface extends Collection {} \ No newline at end of file diff --git a/src/models/external-docs.ts b/src/models/external-docs.ts new file mode 100644 index 000000000..891d53392 --- /dev/null +++ b/src/models/external-docs.ts @@ -0,0 +1,8 @@ +import type { BaseModel } from "./base"; +import type { DescriptionMixinInterface, ExtensionsMixinInterface } from './mixins' + +export interface ExternalDocumentationInterface + extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface { + + url(): string; +} \ No newline at end of file diff --git a/src/models/info.ts b/src/models/info.ts index 8909b8bfd..64c97df5a 100644 --- a/src/models/info.ts +++ b/src/models/info.ts @@ -1,12 +1,18 @@ -import { ContactInterface } from "./contact"; -import { LicenseInterface } from "./license"; -import { BaseModel } from "./base"; +import type { ContactInterface } from "./contact"; +import type { LicenseInterface } from "./license"; +import type { BaseModel } from "./base"; -export interface InfoInterface extends BaseModel { - title(): string; - version(): string; - description(): string; - termsOfService(): string; - contact(): ContactInterface | undefined; - license(): LicenseInterface | undefined; -} \ No newline at end of file +import type { DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface } from "./mixins"; + +export interface InfoInterface extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface { + title(): string; + version(): string; + hasId(): boolean; + id(): string | undefined; + hasTermsOfService(): boolean; + termsOfService(): string | undefined; + hasContact(): boolean; + contact(): ContactInterface | undefined; + hasLicense(): boolean; + license(): LicenseInterface | undefined; +} diff --git a/src/models/license.ts b/src/models/license.ts index aeb50ee7e..b98f88e2a 100644 --- a/src/models/license.ts +++ b/src/models/license.ts @@ -1,6 +1,9 @@ -import { BaseModel } from "./base"; +import type { BaseModel } from "./base"; -export interface LicenseInterface extends BaseModel { - name(): string; - url(): string; -} \ No newline at end of file +import type { ExtensionsMixinInterface } from "./mixins"; + +export interface LicenseInterface extends BaseModel, ExtensionsMixinInterface { + name(): string; + hasUrl(): boolean; + url(): string | undefined; +} diff --git a/src/models/mixins.ts b/src/models/mixins.ts new file mode 100644 index 000000000..7cd305208 --- /dev/null +++ b/src/models/mixins.ts @@ -0,0 +1,26 @@ +import type { BindingsInterface } from './bindings'; +import type { ExtensionsInterface } from './extensions'; +import type { ExternalDocumentationInterface } from './external-docs'; +import type { TagsInterface } from './tags'; + +export interface BindingsMixinInterface { + bindings(): BindingsInterface; +} + +export interface DescriptionMixinInterface { + hasDescription(): boolean; + description(): string | undefined; +} + +export interface ExtensionsMixinInterface { + extensions(): ExtensionsInterface; +} + +export interface ExternalDocumentationMixinInterface { + hasExternalDocs(): boolean; + externalDocs(): ExternalDocumentationInterface | undefined; +} + +export interface TagsMixinInterface { + tags(): TagsInterface; +} \ No newline at end of file diff --git a/src/models/mixins/bindings.ts b/src/models/mixins/bindings.ts deleted file mode 100644 index 41ec9a5d0..000000000 --- a/src/models/mixins/bindings.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { BaseModel } from "../base"; - -export interface BindingsMixinInterface { - hasBindings(): boolean; - hasBindings(protocol: string): boolean; - bindings(): any[]; // TODO: Change type to Tag - bindings(protocol: string): any; // TODO: Change type to Tag -} - -export abstract class BindingsMixin extends BaseModel implements BindingsMixinInterface { - hasBindings(): boolean; - hasBindings(protocol: string): boolean; - hasBindings(protocol?: string): boolean { - const bindings = this.bindings(protocol!); - if (typeof protocol === 'string') { - return Boolean(bindings); - } - return Object.keys(bindings || {}).length > 0; - }; - - - bindings(): any[]; - bindings(protocol: string): any; - bindings(protocol?: string): any | any[] { - if (typeof protocol === 'string') { - if (this._json.bindings && typeof this._json.bindings === 'object') { - return this._json.bindings[protocol]; - } - return; - } - return this._json.bindings || {}; - }; -} diff --git a/src/models/mixins/external-docs.ts b/src/models/mixins/external-docs.ts deleted file mode 100644 index 6dd0e3969..000000000 --- a/src/models/mixins/external-docs.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { BaseModel } from "../base"; - -export interface ExternalDocsMixinInterface { - hasExternalDocs(): boolean; - externalDocs(): any; // TODO: Change type to ExternalDocs -} - -export abstract class ExternalDocsMixin extends BaseModel implements ExternalDocsMixinInterface { - hasExternalDocs(): boolean { - return !!(this._json.externalDocs && Object.keys(this._json.externalDocs).length); - }; - - // TODO: implement it when the ExternalDocs class will be implemented - externalDocs(): any { - return; - }; -} diff --git a/src/models/mixins/specification-extensions.ts b/src/models/mixins/specification-extensions.ts deleted file mode 100644 index 873e2fe45..000000000 --- a/src/models/mixins/specification-extensions.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BaseModel } from "../base"; - -import { EXTENSION_REGEX } from '../../constants'; - -export interface SpecificationExtensionsMixinInterface { - hasExtensions(): boolean; - hasExtensions(name: string): boolean; - extensions(): Record; - extensions(name: string): any; -} - -export abstract class SpecificationExtensionsMixin extends BaseModel implements SpecificationExtensionsMixinInterface { - hasExtensions(): boolean; - hasExtensions(name: string): boolean; - hasExtensions(name?: string): boolean { - const extensions = this.extensions(name!); - if (typeof name === 'string') { - return Boolean(extensions); - } - return Object.keys(extensions || {}).length > 0; - }; - - extensions(): any[]; - extensions(name: string): any; - extensions(name?: string): any | any[] { - if (typeof name === 'string') { - name = name.startsWith('x-') ? name : `x-${name}`; - return this._json[name]; - } - - const result: Record = {}; - Object.entries(this._json).forEach(([key, value]) => { - if (EXTENSION_REGEX.test(key)) { - result[String(key)] = value; - } - }); - return result; - }; -} diff --git a/src/models/mixins/tags.ts b/src/models/mixins/tags.ts deleted file mode 100644 index 2554b39a3..000000000 --- a/src/models/mixins/tags.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { BaseModel } from "../base"; - -export interface TagsMixinInterface { - hasTags(): boolean; - hasTags(name: string): boolean; - tags(): any[]; // TODO: Change type to Tag - tags(name: string): any; // TODO: Change type to Tag -} - -export abstract class TagsMixin extends BaseModel implements TagsMixinInterface { - hasTags(): boolean; - hasTags(name: string): boolean; - hasTags(name?: string): boolean { - if (!Array.isArray(this._json.tags) || !this._json.tags.length) { - return false; - } - if (typeof name === 'string') { - return this._json.tags.some((t: any) => t.name === name); - } - return true; - }; - - - // TODO: return instance(s) of Tag model when the Tag class will be implemented - tags(): any[]; // TODO: Change type to Tag - tags(name: string): any; // TODO: Change type to Tag - tags(name?: string): any | any[] { // TODO: Change type to Tag - if (typeof name === 'string') { - if (Array.isArray(this._json.tags)) { - return this._json.tags.find((t: any) => t.name === name); - } - return; - } - return this._json.tags || []; - }; -} diff --git a/src/models/tag.ts b/src/models/tag.ts new file mode 100644 index 000000000..32bd1fcea --- /dev/null +++ b/src/models/tag.ts @@ -0,0 +1,8 @@ +import type { BaseModel } from "./base"; +import type { DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface } from './mixins' + +export interface TagInterface + extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface { + + name(): string; +} \ No newline at end of file diff --git a/src/models/tags.ts b/src/models/tags.ts new file mode 100644 index 000000000..ceaa5df2b --- /dev/null +++ b/src/models/tags.ts @@ -0,0 +1,4 @@ +import type { Collection } from './collection'; +import type { TagInterface } from './tag'; + +export interface TagsInterface extends Collection {} \ No newline at end of file diff --git a/src/models/mixins/index.ts b/src/models/utils.ts similarity index 68% rename from src/models/mixins/index.ts rename to src/models/utils.ts index 6a3397514..0b73d948a 100644 --- a/src/models/mixins/index.ts +++ b/src/models/utils.ts @@ -1,10 +1,4 @@ -import type { BaseModel } from '../base'; - -export * from './bindings'; -export * from './description'; -export * from './external-docs'; -export * from './specification-extensions'; -export * from './tags'; +import type { BaseModel } from './base'; export interface Constructor extends Function { new (...any: any[]): T; @@ -18,22 +12,22 @@ export function Mixin(a: typeof BaseModel): typeof BaseModel; export function Mixin(a: typeof BaseModel, b: MixinType): typeof BaseModel & Constructor; export function Mixin(a: typeof BaseModel, b: MixinType, c: MixinType): typeof BaseModel & Constructor & Constructor; export function Mixin(a: typeof BaseModel, b: MixinType, c: MixinType, d: MixinType): typeof BaseModel & Constructor & Constructor & Constructor; -export function Mixin(a: typeof BaseModel, b: MixinType, c: MixinType, d: MixinType, e: MixinType): typeof BaseModel & Constructor & Constructor & Constructor & Constructor; +export function Mixin(a: typeof BaseModel, b: MixinType, c: MixinType, d: MixinType, e: MixinType): typeof BaseModel & Constructor & Constructor & Constructor & Constructor; export function Mixin(a: typeof BaseModel, b: MixinType, c: MixinType, d: MixinType, e: MixinType, f: MixinType): typeof BaseModel & Constructor & Constructor & Constructor & Constructor & Constructor; -export function Mixin(baseModel: typeof BaseModel, ...constructors: any[]) { - return mixin(class extends baseModel {}, constructors); +export function Mixin(baseCtor: typeof BaseModel, ...constructors: any[]) { + return mixin(class extends baseCtor {}, constructors); } function mixin(derivedCtor: any, constructors: any[]): typeof BaseModel { - constructors.forEach((baseCtor) => { - Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => { + constructors.forEach((ctor) => { + Object.getOwnPropertyNames(ctor.prototype).forEach((name) => { if (name === 'constructor') { return; } Object.defineProperty( derivedCtor.prototype, name, - Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || Object.create(null), + Object.getOwnPropertyDescriptor(ctor.prototype, name) || Object.create(null), ); }); }); diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index 7614fd5fc..e690f3fcb 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -1,18 +1,20 @@ -import { AsyncAPIDocumentInterface } from "../../models"; import { BaseModel } from "../base"; import { Info } from "./info"; -import { Mixin, ExternalDocsMixin, SpecificationExtensionsMixin, TagsMixin } from '../mixins'; +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; + +import { AsyncAPIDocumentInterface, InfoInterface } from "../../models"; export class AsyncAPIDocument - extends Mixin(BaseModel, ExternalDocsMixin, SpecificationExtensionsMixin, TagsMixin) + extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { version(): string { - return this.json("asyncapi"); + return this._json.asyncapi; } - info(): Info { - return new Info(this.json("info")); + info(): InfoInterface { + return new Info(this._json.info); } } diff --git a/src/models/v2/contact.ts b/src/models/v2/contact.ts index d16af7df3..c7ecb5122 100644 --- a/src/models/v2/contact.ts +++ b/src/models/v2/contact.ts @@ -1,16 +1,32 @@ -import { ContactInterface } from "../../models/contact"; import { BaseModel } from "../base"; -export class Contact extends BaseModel implements ContactInterface { - name(): string { - return this.json("name"); - } +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; - url(): string { - return this.json("url"); - } +import type { ContactInterface } from "../../models/contact"; - email(): string { - return this.json("email"); - } +export class Contact extends Mixin(BaseModel, ExtensionsMixin) implements ContactInterface { + hasName(): boolean { + return !!this._json.name; + } + + name(): string | undefined { + return this._json.name; + } + + hasUrl(): boolean { + return !!this._json.url; + } + + url(): string | undefined { + return this._json.url; + } + + hasEmail(): boolean { + return !!this._json.email; + } + + email(): string | undefined { + return this._json.email; + } } \ No newline at end of file diff --git a/src/models/v2/index.ts b/src/models/v2/index.ts index a06d7d121..34c57b438 100644 --- a/src/models/v2/index.ts +++ b/src/models/v2/index.ts @@ -1,4 +1,8 @@ export { AsyncAPIDocument as AsyncAPIDocumentV2 } from './asyncapi'; export { Contact as ContactV2 } from './contact'; export { Info as InfoV2 } from './info'; -export { License as LicenseV2 } from './license'; \ No newline at end of file +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'; \ No newline at end of file diff --git a/src/models/v2/info.ts b/src/models/v2/info.ts index d6f594e8b..9c12dd292 100644 --- a/src/models/v2/info.ts +++ b/src/models/v2/info.ts @@ -1,32 +1,60 @@ -import { InfoInterface } from "../../models/info"; import { BaseModel } from "../base"; import { Contact } from "./contact"; import { License } from "./license"; -export class Info extends BaseModel implements InfoInterface { - title(): string { - return this.json("title"); - } +import { Mixin } from '../utils'; +import { DescriptionMixin } from './mixins/description'; +import { ExtensionsMixin } from './mixins/extensions'; +import { ExternalDocumentationMixin } from './mixins/external-docs'; +import { TagsMixin } from './mixins/tags'; - version(): string { - return this.json("version"); - } +import type { InfoInterface } from "../../models/info"; - description(): string { - return this.json("description"); - } +export class Info + extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) + implements InfoInterface { - termsOfService(): string { - return this.json("termsOfService"); - } + title(): string { + return this._json.title; + } - contact(): Contact | undefined { - const doc = this.json("contact"); - return doc && new Contact(doc); - } + version(): string { + return this._json.version; + } - license(): License | undefined { - const doc = this.json("license"); - return doc && new License(doc); - } + // TODO: Implement it + id(): string | undefined { + return; + } + + // TODO: Implement it + hasId(): boolean { + return true; + } + + hasTermsOfService(): boolean { + return !!this._json.termsOfService; + } + + termsOfService(): string | undefined { + return this._json.termsOfService; + } + + hasContact(): boolean { + return Object.keys(this._json.contact || {}).length > 0; + } + + contact(): Contact | undefined { + const contact = this._json.contact; + return contact && new Contact(contact); + } + + hasLicense(): boolean { + return Object.keys(this._json.license || {}).length > 0; + } + + license(): License | undefined { + const license = this._json.license; + return license && new License(license); + } } \ No newline at end of file diff --git a/src/models/v2/license.ts b/src/models/v2/license.ts index 67eb74d97..834f43b29 100644 --- a/src/models/v2/license.ts +++ b/src/models/v2/license.ts @@ -1,12 +1,20 @@ -import { LicenseInterface } from "../../models/license"; import { BaseModel } from "../base"; -export class License extends BaseModel implements LicenseInterface { - name(): string { - return this.json("name"); - } +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; - url(): string { - return this.json("url"); - } +import type { LicenseInterface } from "../../models/license"; + +export class License extends Mixin(BaseModel, ExtensionsMixin) implements LicenseInterface { + name(): string { + return this._json.name; + } + + hasUrl(): boolean { + return !!this._json.url; + } + + url(): string | undefined { + return this._json.url; + } } \ No newline at end of file diff --git a/src/models/v2/mixins/bindings.ts b/src/models/v2/mixins/bindings.ts new file mode 100644 index 000000000..13dd7a228 --- /dev/null +++ b/src/models/v2/mixins/bindings.ts @@ -0,0 +1,51 @@ +import { BaseModel } from "../../base"; +import { Collection } from '../../collection'; + +import { Mixin } from '../../utils'; +import { ExtensionsMixin } from './extensions'; + +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( + private readonly _protocol: string, + _json: Record, + ) { + super(_json); + } + + protocol(): string { + return this._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 { + const bindings: Record = this._json.bindings || {}; + return new Bindings( + Object.entries(bindings).map(([protocol, binding]) => new Binding(protocol, binding)) + ); + } +} diff --git a/src/models/mixins/description.ts b/src/models/v2/mixins/description.ts similarity index 61% rename from src/models/mixins/description.ts rename to src/models/v2/mixins/description.ts index f97746b3f..c60f5c8e7 100644 --- a/src/models/mixins/description.ts +++ b/src/models/v2/mixins/description.ts @@ -1,9 +1,6 @@ -import { BaseModel } from "../base"; +import { BaseModel } from "../../base"; -export interface DescriptionMixinInterface { - hasDescription(): boolean; - description(): string | undefined; -} +import type { DescriptionMixinInterface } from "../../mixins"; export abstract class DescriptionMixin extends BaseModel implements DescriptionMixinInterface { hasDescription() { @@ -13,4 +10,4 @@ export abstract class DescriptionMixin extends BaseModel implements DescriptionM 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 new file mode 100644 index 000000000..61ee70409 --- /dev/null +++ b/src/models/v2/mixins/extensions.ts @@ -0,0 +1,51 @@ +import { Collection } from '../../collection'; +import { BaseModel } 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( + private readonly _id: string, + _json: Record, + ) { + super(_json); + } + + id(): string { + return this._id; + } + + version(): string { + return 'to implement'; + } + + value(): any { + return this._json; + } +} + +export class Extensions extends Collection implements ExtensionsInterface { + override get(name: string): ExtensionInterface | undefined { + return this.collections.find(ext => ext.id() === name); + }; + + override has(name: string): boolean { + return this.collections.some(ext => ext.id() === 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(new Extension(key, value)); + } + }); + 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 new file mode 100644 index 000000000..d400201ca --- /dev/null +++ b/src/models/v2/mixins/external-docs.ts @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..6cdee98b9 --- /dev/null +++ b/src/models/v2/mixins/tags.ts @@ -0,0 +1,37 @@ +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 { + const tags = this._json.tags || []; + return new Tags(tags.map((tag: any) => new Tag(tag))); + } +} \ No newline at end of file diff --git a/src/models/v3/asyncapi.ts b/src/models/v3/asyncapi.ts index 113a738fe..f48ee2ea6 100644 --- a/src/models/v3/asyncapi.ts +++ b/src/models/v3/asyncapi.ts @@ -2,10 +2,11 @@ import { AsyncAPIDocumentInterface } from "../../models/asyncapi"; import { BaseModel } from "../base"; import { Info } from "./info"; -import { Mixin, ExternalDocsMixin, SpecificationExtensionsMixin, TagsMixin } from '../mixins'; +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; export class AsyncAPIDocument - extends Mixin(BaseModel, ExternalDocsMixin, SpecificationExtensionsMixin, TagsMixin) + extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { version(): string { diff --git a/src/models/v3/contact.ts b/src/models/v3/contact.ts index d16af7df3..c7ecb5122 100644 --- a/src/models/v3/contact.ts +++ b/src/models/v3/contact.ts @@ -1,16 +1,32 @@ -import { ContactInterface } from "../../models/contact"; import { BaseModel } from "../base"; -export class Contact extends BaseModel implements ContactInterface { - name(): string { - return this.json("name"); - } +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; - url(): string { - return this.json("url"); - } +import type { ContactInterface } from "../../models/contact"; - email(): string { - return this.json("email"); - } +export class Contact extends Mixin(BaseModel, ExtensionsMixin) implements ContactInterface { + hasName(): boolean { + return !!this._json.name; + } + + name(): string | undefined { + return this._json.name; + } + + hasUrl(): boolean { + return !!this._json.url; + } + + url(): string | undefined { + return this._json.url; + } + + hasEmail(): boolean { + return !!this._json.email; + } + + email(): string | undefined { + return this._json.email; + } } \ No newline at end of file diff --git a/src/models/v3/index.ts b/src/models/v3/index.ts index 7be2b264d..967ba16c8 100644 --- a/src/models/v3/index.ts +++ b/src/models/v3/index.ts @@ -1,4 +1,8 @@ export { AsyncAPIDocument as AsyncAPIDocumentV3 } from './asyncapi'; export { Contact as ContactV3 } from './contact'; export { Info as InfoV3 } from './info'; -export { License as LicenseV3 } from './license'; \ No newline at end of file +export { License as LicenseV3 } from './license'; +export { Bindings as BindingsV3, Binding as BindingV3 } from './mixins/bindings'; +export { Extensions as ExtensionsV3, Extension as ExtensionV3 } from './mixins/extensions'; +export { ExternalDocumentation as ExternalDocumentationV3 } from './mixins/external-docs'; +export { Tags as TagsV3, Tag as TagV3 } from './mixins/tags'; \ No newline at end of file diff --git a/src/models/v3/info.ts b/src/models/v3/info.ts index d6f594e8b..9c12dd292 100644 --- a/src/models/v3/info.ts +++ b/src/models/v3/info.ts @@ -1,32 +1,60 @@ -import { InfoInterface } from "../../models/info"; import { BaseModel } from "../base"; import { Contact } from "./contact"; import { License } from "./license"; -export class Info extends BaseModel implements InfoInterface { - title(): string { - return this.json("title"); - } +import { Mixin } from '../utils'; +import { DescriptionMixin } from './mixins/description'; +import { ExtensionsMixin } from './mixins/extensions'; +import { ExternalDocumentationMixin } from './mixins/external-docs'; +import { TagsMixin } from './mixins/tags'; - version(): string { - return this.json("version"); - } +import type { InfoInterface } from "../../models/info"; - description(): string { - return this.json("description"); - } +export class Info + extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) + implements InfoInterface { - termsOfService(): string { - return this.json("termsOfService"); - } + title(): string { + return this._json.title; + } - contact(): Contact | undefined { - const doc = this.json("contact"); - return doc && new Contact(doc); - } + version(): string { + return this._json.version; + } - license(): License | undefined { - const doc = this.json("license"); - return doc && new License(doc); - } + // TODO: Implement it + id(): string | undefined { + return; + } + + // TODO: Implement it + hasId(): boolean { + return true; + } + + hasTermsOfService(): boolean { + return !!this._json.termsOfService; + } + + termsOfService(): string | undefined { + return this._json.termsOfService; + } + + hasContact(): boolean { + return Object.keys(this._json.contact || {}).length > 0; + } + + contact(): Contact | undefined { + const contact = this._json.contact; + return contact && new Contact(contact); + } + + hasLicense(): boolean { + return Object.keys(this._json.license || {}).length > 0; + } + + license(): License | undefined { + const license = this._json.license; + return license && new License(license); + } } \ No newline at end of file diff --git a/src/models/v3/license.ts b/src/models/v3/license.ts index 67eb74d97..834f43b29 100644 --- a/src/models/v3/license.ts +++ b/src/models/v3/license.ts @@ -1,12 +1,20 @@ -import { LicenseInterface } from "../../models/license"; import { BaseModel } from "../base"; -export class License extends BaseModel implements LicenseInterface { - name(): string { - return this.json("name"); - } +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; - url(): string { - return this.json("url"); - } +import type { LicenseInterface } from "../../models/license"; + +export class License extends Mixin(BaseModel, ExtensionsMixin) implements LicenseInterface { + name(): string { + return this._json.name; + } + + hasUrl(): boolean { + return !!this._json.url; + } + + url(): string | undefined { + return this._json.url; + } } \ No newline at end of file diff --git a/src/models/v3/mixins/bindings.ts b/src/models/v3/mixins/bindings.ts new file mode 100644 index 000000000..dc6770263 --- /dev/null +++ b/src/models/v3/mixins/bindings.ts @@ -0,0 +1,51 @@ +import { BaseModel } from "../../base"; +import { Collection } from '../../collection'; + +import { Mixin } from '../../utils'; +import { ExtensionsMixin } from './extensions'; + +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( + private readonly _protocol: string, + _json: Record, + ) { + super(_json); + } + + protocol(): string { + return this._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 { + const bindings: Record = this._json.bindings || {}; + return new Bindings( + Object.entries(bindings).map(([protocol, binding]) => new Binding(protocol, binding)) + ); + } +} \ No newline at end of file diff --git a/src/models/v3/mixins/description.ts b/src/models/v3/mixins/description.ts new file mode 100644 index 000000000..c60f5c8e7 --- /dev/null +++ b/src/models/v3/mixins/description.ts @@ -0,0 +1,13 @@ +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/v3/mixins/extensions.ts b/src/models/v3/mixins/extensions.ts new file mode 100644 index 000000000..61ee70409 --- /dev/null +++ b/src/models/v3/mixins/extensions.ts @@ -0,0 +1,51 @@ +import { Collection } from '../../collection'; +import { BaseModel } 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( + private readonly _id: string, + _json: Record, + ) { + super(_json); + } + + id(): string { + return this._id; + } + + version(): string { + return 'to implement'; + } + + value(): any { + return this._json; + } +} + +export class Extensions extends Collection implements ExtensionsInterface { + override get(name: string): ExtensionInterface | undefined { + return this.collections.find(ext => ext.id() === name); + }; + + override has(name: string): boolean { + return this.collections.some(ext => ext.id() === 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(new Extension(key, value)); + } + }); + return new Extensions(extensions); + }; +} \ No newline at end of file diff --git a/src/models/v3/mixins/external-docs.ts b/src/models/v3/mixins/external-docs.ts new file mode 100644 index 000000000..d400201ca --- /dev/null +++ b/src/models/v3/mixins/external-docs.ts @@ -0,0 +1,30 @@ +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/v3/mixins/tags.ts b/src/models/v3/mixins/tags.ts new file mode 100644 index 000000000..6cdee98b9 --- /dev/null +++ b/src/models/v3/mixins/tags.ts @@ -0,0 +1,37 @@ +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 { + const tags = this._json.tags || []; + return new Tags(tags.map((tag: any) => new Tag(tag))); + } +} \ No newline at end of file diff --git a/test/models/collection.spec.ts b/test/models/collection.spec.ts new file mode 100644 index 000000000..f3a5e45bd --- /dev/null +++ b/test/models/collection.spec.ts @@ -0,0 +1,79 @@ +import { BaseModel } from '../../src/models/base'; +import { Collection } from '../../src/models/collection'; + +describe('Collection model', function() { + class ItemModel extends BaseModel { + name(): string | undefined { + return this._json.name; + } + }; + + class Model extends Collection { + override get(name: string): ItemModel | undefined { + return this.collections.find(item => item.name() === name); + }; + + override has(name: string): boolean { + return this.collections.some(item => item.name() === name); + }; + }; + + describe('.isEmpty()', function() { + it('should return true if collection is empty', function() { + const d = new Model([]); + expect(d.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function() { + const doc = { name: 'name' }; + const item = new ItemModel(doc); + const d = new Model([item]); + expect(d.isEmpty()).toEqual(false); + }); + }); + + describe('.all()', function() { + it('should return the whole collections', function() { + const doc1 = { name: 'name1' }; + const doc2 = { name: 'name1' }; + const item1 = new ItemModel(doc1); + const item2 = new ItemModel(doc2); + const d = new Model([item1, item2]); + expect(d.all().length).toEqual(2); + expect(d.all()).toEqual([item1, item2]); + }); + }); + + describe('.has()', function() { + it('should return true if collection has given item', function() { + const doc = { name: 'name' }; + const item = new ItemModel(doc); + const d = new Model([item]); + expect(d.has('name')).toEqual(true); + }); + + it('should return false if collection has not given item', function() { + const doc = { name: 'name' }; + const item = new ItemModel(doc); + const d = new Model([item]); + expect(d.has('name1')).toEqual(false); + }); + }); + + describe('.get()', function() { + it('should return instance of ItemModel if collection has given item', function() { + const doc = { name: 'name' }; + const item = new ItemModel(doc); + const d = new Model([item]); + expect(d.get('name')).toBeInstanceOf(ItemModel); + expect(d.get('name')).toEqual(item); + }); + + it('should return undefined if collection has not given item', function() { + const doc = { name: 'name' }; + const item = new ItemModel(doc); + const d = new Model([item]); + expect(d.get('name1')).toEqual(undefined); + }); + }); +}); diff --git a/test/models/mixins/bindings.spec.ts b/test/models/mixins/bindings.spec.ts deleted file mode 100644 index 6438e391d..000000000 --- a/test/models/mixins/bindings.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { BaseModel } from '../../../src/models/base'; -import { BindingsMixin, Mixin } from '../../../src/models/mixins'; - -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 mixin', function() { - describe('.hasBindings()', function() { - it('should return a boolean indicating if the object has bindings', function() { - expect(d1.hasBindings()).toEqual(true); - expect(d2.hasBindings()).toEqual(false); - expect(d3.hasBindings()).toEqual(false); - }); - - it('should return a boolean indicating if the bindings object has appropriate binding by name', function() { - expect(d1.hasBindings('amqp')).toEqual(true); - expect(d1.hasBindings('http')).toEqual(false); - expect(d2.hasBindings('amqp')).toEqual(false); - expect(d3.hasBindings('amqp')).toEqual(false); - }); - }); - - describe('.bindings()', function() { - it('should return a map of bindings', function() { - expect(d1.bindings()).toEqual(doc1.bindings); - }); - - it('should return an empty object', function() { - expect(d2.bindings()).toEqual({}); - expect(d3.bindings()).toEqual({}); - }); - - it('should return a binding object', function() { - expect(d1.bindings('amqp')).toEqual(doc1.bindings.amqp); - }); - - it('should return a undefined', function() { - expect(d1.bindings('http')).toEqual(undefined); - expect(d2.bindings('amqp')).toEqual(undefined); - expect(d3.bindings('amqp')).toEqual(undefined); - }); - }); -}); diff --git a/test/models/mixins/external-docs.spec.ts b/test/models/mixins/external-docs.spec.ts deleted file mode 100644 index ce722b893..000000000 --- a/test/models/mixins/external-docs.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { BaseModel } from '../../../src/models/base'; -import { ExternalDocsMixin, Mixin } from '../../../src/models/mixins'; - -class Model extends Mixin(BaseModel, ExternalDocsMixin) {}; - -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('ExternalDocs mixin', function() { - 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); - }); - }); - - // TODO: implement it when the ExternalDocs class will be implemented - describe('.externalDocs()', function() { - // it('should return a externalDocs object', function() { - // expect(d1.externalDocs() instanceof ExternalDocs).toEqual(true); - // expect(d1.externalDocs().json()).toEqual(doc1.externalDocs); - - // expect(d2.externalDocs() instanceof ExternalDocs).toEqual(true); - // expect(d2.externalDocs().json()).toEqual(doc2.externalDocs); - // }); - - it('should return a undefined', function() { - expect(d3.externalDocs()).toEqual(undefined); - }); - }); -}); diff --git a/test/models/mixins/specification-extensions.spec.ts b/test/models/mixins/specification-extensions.spec.ts deleted file mode 100644 index 3b08b33ab..000000000 --- a/test/models/mixins/specification-extensions.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BaseModel } from '../../../src/models/base'; -import { Mixin, SpecificationExtensionsMixin } from '../../../src/models/mixins'; - -class Model extends Mixin(BaseModel, SpecificationExtensionsMixin) {}; - -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('SpecificationExtensions mixin', function() { - describe('.hasExtensions()', function() { - it('should return a boolean indicating if the object has extensions', function() { - expect(d1.hasExtensions()).toEqual(true); - expect(d2.hasExtensions()).toEqual(false); - expect(d3.hasExtensions()).toEqual(false); - }); - - it('should return a boolean indicating if the object has appropriate extension by key', function() { - expect(d1.hasExtensions('x-test')).toEqual(true); - expect(d1.hasExtensions('x-test2')).toEqual(false); - expect(d2.hasExtensions('x-test')).toEqual(false); - expect(d3.hasExtensions('x-test')).toEqual(false); - }); - - it('should return a boolean indicating if the object has appropriate extension by key (without x- prefix)', function() { - expect(d1.hasExtensions('test')).toEqual(true); - expect(d1.hasExtensions('test2')).toEqual(false); - expect(d2.hasExtensions('test')).toEqual(false); - expect(d3.hasExtensions('test')).toEqual(false); - }); - }); - - describe('.extensions()', function() { - it('should return a object with extensions', function() { - expect(d1.extensions()).toEqual({ 'x-test': 'testing' }); - }); - - it('should return a empty object', function() { - expect(d2.extensions()).toEqual({}); - expect(d3.extensions()).toEqual({}); - }); - - it('should return a value by key', function() { - expect(d1.extensions('x-test')).toEqual('testing'); - }); - - it('should return a value by key (without x- prefix)', function() { - expect(d1.extensions('test')).toEqual('testing'); - }); - - it('should return an undefined', function() { - expect(d1.extensions('x-test2')).toEqual(undefined); - expect(d2.extensions('x-test')).toEqual(undefined); - expect(d3.extensions('x-test')).toEqual(undefined); - }); - }); -}); diff --git a/test/models/mixins/tags.spec.ts b/test/models/mixins/tags.spec.ts deleted file mode 100644 index c1389391d..000000000 --- a/test/models/mixins/tags.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { BaseModel } from '../../../src/models/base'; -import { Mixin, TagsMixin } from '../../../src/models/mixins'; - -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 mixin', function() { - describe('#hasTags()', function() { - it('should return a boolean indicating if the object has tags', function() { - expect(d1.hasTags()).toEqual(true); - expect(d2.hasTags()).toEqual(false); - expect(d3.hasTags()).toEqual(false); - }); - - it('should return a boolean indicating if the tags object has appropriate tag by name', function() { - expect(d1.hasTags('test1')).toEqual(true); - expect(d1.hasTags('test2')).toEqual(true); - expect(d1.hasTags('test3')).toEqual(false); - expect(d2.hasTags('test1')).toEqual(false); - expect(d3.hasTags('test1')).toEqual(false); - }); - }); - - describe('#tags()', function() { - it('should return an array of tag objects', function() { - expect(Array.isArray(d1.tags())).toEqual(true); - d1.tags().forEach((tag, i) => { - expect(tag).toEqual(doc1.tags[i]); - }); - }); - - it('should return an empty array', function() { - expect(d2.tags()).toEqual([]); - expect(d3.tags()).toEqual([]); - }); - - it('should return a tag object', function() { - expect(d1.tags('test1')).not.toEqual(undefined); - expect(d1.tags('test1')).toEqual(doc1.tags[0]); - expect(d1.tags('test2')).not.toEqual(undefined); - expect(d1.tags('test2')).toEqual(doc1.tags[1]); - }); - it('should return a undefined', function() { - expect(d1.tags('test3')).toEqual(undefined); - expect(d2.tags('test1')).toEqual(undefined); - expect(d3.tags('test1')).toEqual(undefined); - }); - }); -}); diff --git a/test/models/v2/asyncapi.spec.ts b/test/models/v2/asyncapi.spec.ts index 662938fd0..24c98cbc9 100644 --- a/test/models/v2/asyncapi.spec.ts +++ b/test/models/v2/asyncapi.spec.ts @@ -1,10 +1,8 @@ import { newAsyncAPIDocument, AsyncAPIDocumentV2, InfoV2, AsyncAPIDocumentV3 } from '../../../src/models'; import { - assertExternalDocsMixinInheritance, - assertSpecificationExtensionsMixinInheritance, - assertTagsMixinInheritance, -} from '../mixins/inheritance'; + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; describe('AsyncAPIDocument model', function() { describe('.version()', function() { @@ -29,10 +27,8 @@ describe('AsyncAPIDocument model', function() { }); }); - describe('mixins', function() { - assertExternalDocsMixinInheritance(AsyncAPIDocumentV2); - assertSpecificationExtensionsMixinInheritance(AsyncAPIDocumentV2); - assertTagsMixinInheritance(AsyncAPIDocumentV2); + describe('mixins inheritance', function() { + assertExtensionsMixinInheritance(AsyncAPIDocumentV2); }); }); diff --git a/test/models/v2/contact.spec.ts b/test/models/v2/contact.spec.ts index d9194f3e7..b7b9a8bb6 100644 --- a/test/models/v2/contact.spec.ts +++ b/test/models/v2/contact.spec.ts @@ -1,6 +1,24 @@ import { Contact } from '../../../src/models/v2/contact'; +import { + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; + describe('Contact model', function() { + describe('.hasName()', function() { + it('should return true when there is a value', function() { + const doc = { 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 d = new Contact(doc); + expect(d.hasName()).toEqual(false); + }); + }); + describe('.name()', function() { it('should return the value', function() { const doc = { name: "LeChuck" }; @@ -15,6 +33,20 @@ describe('Contact model', function() { }); }); + describe('.hasUrl()', function() { + it('should return true when there is a value', function() { + const doc = { 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 d = new Contact(doc); + expect(d.hasUrl()).toEqual(false); + }); + }); + describe('.url()', function() { it('should return the value', function() { const doc = { url: "https://example.com" }; @@ -29,6 +61,20 @@ describe('Contact model', function() { }); }); + describe('.hasEmail()', function() { + it('should return true when there is a value', function() { + const doc = { 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 d = new Contact(doc); + expect(d.hasEmail()).toEqual(false); + }); + }); + describe('.email()', function() { it('should return the value', function() { const doc = { email: "lechuck@example.com" }; @@ -42,4 +88,8 @@ describe('Contact model', function() { expect(d.email()).toBeUndefined(); }); }); + + describe('mixins inheritance', function() { + assertExtensionsMixinInheritance(Contact); + }); }); diff --git a/test/models/v2/external-docs.spec.ts b/test/models/v2/external-docs.spec.ts new file mode 100644 index 000000000..579247c5a --- /dev/null +++ b/test/models/v2/external-docs.spec.ts @@ -0,0 +1,21 @@ +import { ExternalDocumentation } from '../../../src/models/v2/mixins/external-docs'; + +import { + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; + +describe('ExternalDocumentation model', function() { + describe('.name()', function() { + it('should return the value', function() { + const doc = { url: 'somewhere' }; + const d = new ExternalDocumentation(doc); + expect(d.url()).toEqual(doc.url); + }); + }); + + describe('mixins inheritance', function() { + assertDescriptionMixinInheritance(ExternalDocumentation); + assertExtensionsMixinInheritance(ExternalDocumentation); + }); +}); diff --git a/test/models/v2/info.spec.ts b/test/models/v2/info.spec.ts index 4d7380629..6d9e97d55 100644 --- a/test/models/v2/info.spec.ts +++ b/test/models/v2/info.spec.ts @@ -1,7 +1,14 @@ -import { Contact } from '../../../src/models/v2/contact'; import { Info } from '../../../src/models/v2/info'; +import { Contact } from '../../../src/models/v2/contact'; import { License } from '../../../src/models/v2/license'; +import { + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, + assertExternalDocumentationMixinInheritance, + assertTagsMixinInheritance +} from './mixins/inheritance'; + describe('Info model', function() { describe('.title()', function() { it('should return the value', function() { @@ -9,12 +16,6 @@ describe('Info model', function() { const d = new Info(doc); expect(d.title()).toEqual(doc.title); }); - - it('should return undefined when there is no value', function() { - const doc = { }; - const d = new Info(doc); - expect(d.title()).toBeUndefined(); - }); }); describe('.version()', function() { @@ -23,25 +24,19 @@ describe('Info model', function() { const d = new Info(doc); expect(d.version()).toEqual(doc.version); }); - - it('should return undefined when there is no value', function() { - const doc = { }; - const d = new Info(doc); - expect(d.version()).toBeUndefined(); - }); }); - describe('.description()', function() { - it('should return the value', function() { - const doc = { description: "This is the API of Example" }; + describe('.hasTermsOfService()', function() { + it('should return true when there is a value', function() { + const doc = { termsOfService: "These are the terms of service" }; const d = new Info(doc); - expect(d.description()).toEqual(doc.description); + expect(d.hasTermsOfService()).toEqual(true); }); - it('should return undefined when there is no value', function() { - const doc = { }; + it('should return false when there is no value', function() { + const doc = {}; const d = new Info(doc); - expect(d.description()).toBeUndefined(); + expect(d.hasTermsOfService()).toEqual(false); }); }); @@ -53,37 +48,72 @@ describe('Info model', function() { }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = {}; const d = new Info(doc); expect(d.termsOfService()).toBeUndefined(); }); }); + + describe('.hasContact()', function() { + it('should return true when there is a value', function() { + const doc = { contact: { name: "LeChuck" } }; + const d = new Info(doc); + expect(d.hasContact()).toEqual(true); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new Info(doc); + expect(d.hasContact()).toEqual(false); + }); + }); describe('.contact()', function() { it('should return a Contact object', function() { const doc = { contact: { name: "LeChuck" } }; const d = new Info(doc); - expect(d.contact() instanceof Contact).toBeTruthy(); + expect(d.contact()).toBeInstanceOf(Contact); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = {}; const d = new Info(doc); expect(d.contact()).toBeUndefined(); }); }); + describe('.hasLicense()', function() { + it('should return true when there is a value', function() { + const doc = { license: { name: "Apache 2.0" } }; + const d = new Info(doc); + expect(d.hasLicense()).toEqual(true); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new Info(doc); + expect(d.hasLicense()).toEqual(false); + }); + }); + describe('.license()', function() { it('should return a License object', function() { const doc = { license: { name: "Apache 2.0" } }; const d = new Info(doc); - expect(d.license() instanceof License).toBeTruthy(); + expect(d.license()).toBeInstanceOf(License); }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = {}; const d = new Info(doc); expect(d.license()).toBeUndefined(); }); }); + + describe('mixins inheritance', function() { + assertDescriptionMixinInheritance(Info); + assertExtensionsMixinInheritance(Info); + assertExternalDocumentationMixinInheritance(Info); + assertTagsMixinInheritance(Info); + }); }); diff --git a/test/models/v2/license.spec.ts b/test/models/v2/license.spec.ts index 992b54791..855ba320a 100644 --- a/test/models/v2/license.spec.ts +++ b/test/models/v2/license.spec.ts @@ -1,5 +1,9 @@ import { License } from '../../../src/models/v2/license'; +import { + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; + describe('License model', function() { describe('.name()', function() { it('should return the value', function() { @@ -7,11 +11,19 @@ describe('License model', function() { const d = new License(doc); expect(d.name()).toEqual(doc.name); }); + }); + + 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 d = new License(doc); + expect(d.hasUrl()).toEqual(true); + }); - it('should return undefined when there is no value', function() { - const doc = { }; + it('should return false when there is no value', function() { + const doc = {}; const d = new License(doc); - expect(d.name()).toBeUndefined(); + expect(d.hasUrl()).toEqual(false); }); }); @@ -23,9 +35,13 @@ describe('License model', function() { }); it('should return undefined when there is no value', function() { - const doc = { }; + const doc = {}; const d = new License(doc); expect(d.url()).toBeUndefined(); }); }); + + describe('mixins inheritance', function() { + assertExtensionsMixinInheritance(License); + }); }); diff --git a/test/models/v2/mixins/bindings.spec.ts b/test/models/v2/mixins/bindings.spec.ts new file mode 100644 index 000000000..cb85bf53d --- /dev/null +++ b/test/models/v2/mixins/bindings.spec.ts @@ -0,0 +1,25 @@ +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/mixins/description.spec.ts b/test/models/v2/mixins/description.spec.ts similarity index 60% rename from test/models/mixins/description.spec.ts rename to test/models/v2/mixins/description.spec.ts index 99027ac2b..32ba29999 100644 --- a/test/models/mixins/description.spec.ts +++ b/test/models/v2/mixins/description.spec.ts @@ -1,16 +1,17 @@ -import { BaseModel } from '../../../src/models/base'; -import { DescriptionMixin, Mixin } from '../../../src/models/mixins'; +import { BaseModel } from '../../../../src/models/base'; +import { Mixin } from '../../../../src/models/utils'; +import { DescriptionMixin } from '../../../../src/models/v2/mixins/description'; -class Model extends Mixin(BaseModel, DescriptionMixin) {}; +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); + 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('Description mixin', function() { describe('.hasDescription()', function() { it('should return a boolean indicating if the object has description', function() { expect(d1.hasDescription()).toEqual(true); @@ -29,4 +30,4 @@ describe('Description mixin', 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 new file mode 100644 index 000000000..51e28ed15 --- /dev/null +++ b/test/models/v2/mixins/extensions.spec.ts @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..6cf9c995a --- /dev/null +++ b/test/models/v2/mixins/external-docs.spec.ts @@ -0,0 +1,36 @@ +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); + }); + }); + + // TODO: implement it when the ExternalDocs class will be implemented + 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/mixins/inheritance.ts b/test/models/v2/mixins/inheritance.ts similarity index 61% rename from test/models/mixins/inheritance.ts rename to test/models/v2/mixins/inheritance.ts index 439027488..d1b473e36 100644 --- a/test/models/mixins/inheritance.ts +++ b/test/models/v2/mixins/inheritance.ts @@ -1,18 +1,12 @@ -import { - BindingsMixin, - DescriptionMixin, - ExternalDocsMixin, - SpecificationExtensionsMixin, - TagsMixin, -} from '../../../src/models/mixins'; +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'; export function assertBindingsMixinInheritance(model: typeof BindingsMixin) { describe('BindingsMixin inheritance', function() { it(`check if ${model.name} model has inherited methods from BindingsMixin`, function() { - expect(model.prototype.hasBindings).not.toEqual(undefined); - expect(typeof model.prototype.hasBindings).toEqual('function'); - expect(model.prototype.hasBindings === BindingsMixin.prototype.hasBindings).toEqual(true); - expect(model.prototype.bindings).not.toEqual(undefined); expect(typeof model.prototype.bindings).toEqual('function'); expect(model.prototype.bindings === BindingsMixin.prototype.bindings).toEqual(true); @@ -34,30 +28,26 @@ export function assertDescriptionMixinInheritance(model: typeof DescriptionMixin }); } -export function assertExternalDocsMixinInheritance(model: typeof ExternalDocsMixin) { +export function assertExtensionsMixinInheritance(model: typeof ExtensionsMixin) { + 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: typeof ExternalDocumentationMixin) { describe('ExternalDocsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from ExternalDocsMixin`, 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 === ExternalDocsMixin.prototype.hasExternalDocs).toEqual(true); + 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 === ExternalDocsMixin.prototype.externalDocs).toEqual(true); - }); - }); -} - -export function assertSpecificationExtensionsMixinInheritance(model: typeof SpecificationExtensionsMixin) { - describe('SpecificationExtensionsMixin inheritance', function() { - it(`check if ${model.name} model has inherited methods from SpecificationExtensionsMixin`, function() { - expect(model.prototype.hasExtensions).not.toEqual(undefined); - expect(typeof model.prototype.hasExtensions).toEqual('function'); - expect(model.prototype.hasExtensions === SpecificationExtensionsMixin.prototype.hasExtensions).toEqual(true); - - expect(model.prototype.extensions).not.toEqual(undefined); - expect(typeof model.prototype.extensions).toEqual('function'); - expect(model.prototype.extensions === SpecificationExtensionsMixin.prototype.extensions).toEqual(true); + expect(model.prototype.externalDocs === ExternalDocumentationMixin.prototype.externalDocs).toEqual(true); }); }); } @@ -65,13 +55,9 @@ export function assertSpecificationExtensionsMixinInheritance(model: typeof Spec export function assertTagsMixinInheritance(model: typeof TagsMixin) { describe('TagsMixin inheritance', function() { it(`check if ${model.name} model has inherited methods from TagsMixin`, function() { - expect(model.prototype.hasTags).not.toEqual(undefined); - expect(typeof model.prototype.hasTags).toEqual('function'); - expect(model.prototype.hasTags === TagsMixin.prototype.hasTags).toEqual(true); - 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 new file mode 100644 index 000000000..29a658254 --- /dev/null +++ b/test/models/v2/mixins/tags.spec.ts @@ -0,0 +1,25 @@ +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/tag.spec.ts b/test/models/v2/tag.spec.ts new file mode 100644 index 000000000..8bda373bc --- /dev/null +++ b/test/models/v2/tag.spec.ts @@ -0,0 +1,23 @@ +import { Tag } from '../../../src/models/v2/mixins/tags'; + +import { + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, + assertExternalDocumentationMixinInheritance +} from './mixins/inheritance'; + +describe('Tag model', function() { + describe('.name()', function() { + it('should return the value', function() { + const doc = { name: "LeChuck" }; + const d = new Tag(doc); + expect(d.name()).toEqual(doc.name); + }); + }); + + describe('mixins inheritance', function() { + assertDescriptionMixinInheritance(Tag); + assertExtensionsMixinInheritance(Tag); + assertExternalDocumentationMixinInheritance(Tag); + }); +});