diff --git a/src/models/asyncapi.ts b/src/models/asyncapi.ts index 76aca11eb..1c2944484 100644 --- a/src/models/asyncapi.ts +++ b/src/models/asyncapi.ts @@ -16,9 +16,9 @@ export interface AsyncAPIDocumentInterface extends BaseModel, ExtensionsMixinInt export function newAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface { switch (asyncapi.semver.major) { case 2: - return new AsyncAPIDocumentV2(asyncapi.parsed, { parent: null, asyncapi, pointer: '/' }); + return new AsyncAPIDocumentV2(asyncapi.parsed, { asyncapi, pointer: '/' }); case 3: - return new AsyncAPIDocumentV3(asyncapi.parsed, { parent: null, asyncapi, pointer: '/' }); + return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' }); default: throw new Error(`Unsupported AsyncAPI version: ${asyncapi.semver.version}`); } diff --git a/src/models/base.ts b/src/models/base.ts index 7e7bb8b7a..ca3efcc85 100644 --- a/src/models/base.ts +++ b/src/models/base.ts @@ -1,10 +1,10 @@ import type { Constructor } from "./utils"; import type { DetailedAsyncAPI } from "../types"; -export interface ModelMetadata

{ +export interface ModelMetadata { asyncapi: DetailedAsyncAPI; pointer: string; - parent: P | null; + [key: string]: any; } export abstract class BaseModel { @@ -32,8 +32,8 @@ export abstract class BaseModel { return `${this._meta?.pointer}/${field}`; } - protected createModel(Model: Constructor, value: any, { id, parent, pointer }: { id?: string, parent?: any, pointer: string | number }): T { - const meta = { asyncapi: this._meta.asyncapi, parent: parent || this, pointer } as ModelMetadata; + protected createModel(Model: Constructor, value: any, { id, parent, pointer, ...rest }: { id?: string, pointer: string | number, [key: string]: any }): T { + const meta = { ...rest, asyncapi: this._meta.asyncapi, pointer } as ModelMetadata; if (id) { return new Model(id, value, meta); } diff --git a/src/models/channel-parameter.ts b/src/models/channel-parameter.ts index bc60be07b..6aec455b9 100644 --- a/src/models/channel-parameter.ts +++ b/src/models/channel-parameter.ts @@ -1,3 +1,11 @@ import type { BaseModel } from "./base"; +import type { DescriptionMixinInterface, ExtensionsMixinInterface } from "./mixins"; +import type { SchemaInterface } from "./schema"; -export interface ChannelParameterInterface extends BaseModel {} +export interface ChannelParameterInterface extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface { + id(): string; + hasSchema(): boolean; + schema(): SchemaInterface | undefined; + hasLocation(): boolean; + location(): string | undefined; +} diff --git a/src/models/channel.ts b/src/models/channel.ts index 75dae611b..3f360afc0 100644 --- a/src/models/channel.ts +++ b/src/models/channel.ts @@ -1,3 +1,13 @@ import type { BaseModel } from "./base"; +import type { ChannelParametersInterface } from "./channel-parameters"; +import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface } from "./mixins"; +import type { OperationsInterface } from "./operations"; +import type { ServersInterface } from "./servers"; -export interface ChannelInterface extends BaseModel {} +export interface ChannelInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface { + id(): string; + address(): string; + servers(): ServersInterface; + operations(): OperationsInterface; + parameters(): ChannelParametersInterface; +} diff --git a/src/models/components.ts b/src/models/components.ts index 9bca87a5a..8e5d16a8c 100644 --- a/src/models/components.ts +++ b/src/models/components.ts @@ -3,14 +3,15 @@ import type { Collection } from './collection'; import type { ServersInterface } from './servers'; import type { ChannelsInterface } from './channels'; import type { OperationsInterface } from './operations'; -import type { OperationsTraitsInterface } from './operation-traits'; +import type { OperationTraitsInterface } from './operation-traits'; import type { MessagesInterface } from './messages'; -import type { MessagesTraitsInterface } from './message-traits'; +import type { MessageTraitsInterface } from './message-traits'; import type { SchemasInterface } from './schemas'; import type { ChannelParametersInterface } from './channel-parameters'; import type { ServerVariablesInterface } from './server-variables'; import type { CorrelationIdInterface } from './correlation-id'; import type { BindingsInterface } from './bindings'; +import type { SecuritySchemesInterface } from './security-schemes'; import type { ExtensionsMixinInterface } from './mixins'; export interface Components extends BaseModel, ExtensionsMixinInterface { @@ -21,10 +22,10 @@ export interface Components extends BaseModel, ExtensionsMixinInterface { schemas(): SchemasInterface; channelParameters(): ChannelParametersInterface; serverVariables(): ServerVariablesInterface; - operationTraits(): OperationsTraitsInterface; - messageTraits(): MessagesTraitsInterface; + operationTraits(): OperationTraitsInterface; + messageTraits(): MessageTraitsInterface; correlationIds(): Collection; - securitySchemes(): any; // TODO: Fix type after merging Souvik PR + securitySchemes(): SecuritySchemesInterface; serverBindings(): Collection; channelBindings(): Collection; operationBindings(): Collection; diff --git a/src/models/correlation-id.ts b/src/models/correlation-id.ts index 347908f40..4f208f92c 100644 --- a/src/models/correlation-id.ts +++ b/src/models/correlation-id.ts @@ -1,3 +1,7 @@ import type { BaseModel } from "./base"; +import type { DescriptionMixinInterface, ExtensionsMixinInterface } from "./mixins"; -export interface CorrelationIdInterface extends BaseModel {} +export interface CorrelationIdInterface extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface { + hasLocation(): boolean; + location(): string | undefined; +} diff --git a/src/models/message-example.ts b/src/models/message-example.ts index 273f24b1c..fe535273f 100644 --- a/src/models/message-example.ts +++ b/src/models/message-example.ts @@ -1,3 +1,13 @@ import type { BaseModel } from "./base"; +import type { ExtensionsMixinInterface } from './mixins'; -export interface MessageExample extends BaseModel {} +export interface MessageExampleInterface extends BaseModel, ExtensionsMixinInterface { + hasName(): boolean; + name(): string; + hasSummary(): boolean; + summary(): string; + hasHeaders(): boolean; + headers(): Record | undefined; + hasPayload(): boolean; + payload(): Record | undefined; +} diff --git a/src/models/message-examples.ts b/src/models/message-examples.ts new file mode 100644 index 000000000..ddf9821f0 --- /dev/null +++ b/src/models/message-examples.ts @@ -0,0 +1,4 @@ +import type { Collection } from "./collection"; +import type { MessageExampleInterface } from "./message-example"; + +export interface MessageExamplesInterface extends Collection {} diff --git a/src/models/message-trait.ts b/src/models/message-trait.ts index b148318c0..27400e7ba 100644 --- a/src/models/message-trait.ts +++ b/src/models/message-trait.ts @@ -1,3 +1,24 @@ import type { BaseModel } from "./base"; +import type { CorrelationIdInterface } from "./correlation-id"; +import type { MessageExamplesInterface } from "./message-examples"; +import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface } from './mixins'; +import type { SchemaInterface } from "./schema"; -export interface MessageTraitInterface extends BaseModel {} +export interface MessageTraitInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface { + id(): string; + hasMessageId(): boolean; + messageId(): string | undefined; + hasCorrelationId(): boolean; + correlationId(): CorrelationIdInterface | undefined; + hasContentType(): boolean; + contentType(): string | undefined; + hasHeaders(): boolean + headers(): SchemaInterface | undefined; + hasName(): boolean; + name(): string | undefined; + hasTitle(): boolean; + title(): string | undefined; + hasSummary(): boolean; + summary(): string | undefined; + examples(): MessageExamplesInterface; +} diff --git a/src/models/message-traits.ts b/src/models/message-traits.ts index 3730bbde7..4a85d226c 100644 --- a/src/models/message-traits.ts +++ b/src/models/message-traits.ts @@ -1,4 +1,4 @@ import type { Collection } from "./collection"; import type { MessageTraitInterface } from "./message-trait"; -export interface MessagesTraitsInterface extends Collection {} \ No newline at end of file +export interface MessageTraitsInterface extends Collection {} \ No newline at end of file diff --git a/src/models/message.ts b/src/models/message.ts index 25c764a1f..ce3293a7c 100644 --- a/src/models/message.ts +++ b/src/models/message.ts @@ -1,4 +1,10 @@ import type { BaseModel } from "./base"; +import type { MessageTraitsInterface } from "./message-traits"; import type { MessageTraitInterface } from "./message-trait"; +import type { SchemaInterface } from "./schema"; -export interface MessageInterface extends BaseModel, MessageTraitInterface {} +export interface MessageInterface extends BaseModel, MessageTraitInterface { + hasPayload(): boolean; + payload(): SchemaInterface | undefined; + traits(): MessageTraitsInterface; +} diff --git a/src/models/oauth-flow.ts b/src/models/oauth-flow.ts index 0fdbc90e6..12f39223b 100644 --- a/src/models/oauth-flow.ts +++ b/src/models/oauth-flow.ts @@ -2,9 +2,9 @@ import { BaseModel } from './base'; import { ExtensionsMixinInterface } from './mixins'; export interface OAuthFlowInterface extends BaseModel, ExtensionsMixinInterface { - authorizationUrl(): string | undefined; - hasRefreshUrl(): boolean; - refreshUrl(): string | undefined; - scopes(): Record | undefined; - tokenUrl(): string | undefined; -} \ No newline at end of file + authorizationUrl(): string | undefined; + hasRefreshUrl(): boolean; + refreshUrl(): string | undefined; + scopes(): Record | undefined; + tokenUrl(): string | undefined; +} diff --git a/src/models/oauth-flows.ts b/src/models/oauth-flows.ts index b9e040d3c..8568c2d8f 100644 --- a/src/models/oauth-flows.ts +++ b/src/models/oauth-flows.ts @@ -1,14 +1,14 @@ -import { OAuthFlowInterface } from './oauth-flow'; -import { BaseModel } from './base'; -import {ExtensionsMixinInterface} from './mixins'; +import type { BaseModel } from './base'; +import type { OAuthFlowInterface } from './oauth-flow'; +import type { ExtensionsMixinInterface } from './mixins'; export interface OAuthFlowsInterface extends BaseModel, ExtensionsMixinInterface { - hasAuthorizationCode(): boolean; - authorizationCode(): OAuthFlowInterface | undefined; - hasClientCredentials(): boolean - clientCredentials(): OAuthFlowInterface | undefined; - hasImplicit(): boolean; - implicit(): OAuthFlowInterface | undefined; - hasPassword(): boolean; - password(): OAuthFlowInterface | undefined; -} \ No newline at end of file + hasAuthorizationCode(): boolean; + authorizationCode(): OAuthFlowInterface | undefined; + hasClientCredentials(): boolean + clientCredentials(): OAuthFlowInterface | undefined; + hasImplicit(): boolean; + implicit(): OAuthFlowInterface | undefined; + hasPassword(): boolean; + password(): OAuthFlowInterface | undefined; +} diff --git a/src/models/operation-trait.ts b/src/models/operation-trait.ts index fe7ca9039..baeae0f32 100644 --- a/src/models/operation-trait.ts +++ b/src/models/operation-trait.ts @@ -1,3 +1,14 @@ import type { BaseModel } from "./base"; +import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface } from './mixins'; +import type { OperationAction } from "./operation"; +import type { SecuritySchemeInterface } from "./security-scheme"; -export interface OperationTraitInterface extends BaseModel {} +export interface OperationTraitInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface { + id(): string; + action(): OperationAction; + hasOperationId(): boolean; + operationId(): string | undefined; + hasSummary(): boolean; + summary(): string | undefined; + security(): Array>; +} diff --git a/src/models/operation-traits.ts b/src/models/operation-traits.ts index 31e8bcc8f..77332b8d3 100644 --- a/src/models/operation-traits.ts +++ b/src/models/operation-traits.ts @@ -1,4 +1,4 @@ import type { Collection } from "./collection"; import type { OperationTraitInterface } from "./operation-trait"; -export interface OperationsTraitsInterface extends Collection {} \ No newline at end of file +export interface OperationTraitsInterface extends Collection {} \ No newline at end of file diff --git a/src/models/operation.ts b/src/models/operation.ts index 5eeaaef04..beccca7cf 100644 --- a/src/models/operation.ts +++ b/src/models/operation.ts @@ -1,4 +1,11 @@ import type { BaseModel } from "./base"; +import type { MessagesInterface } from "./messages"; +import type { OperationTraitsInterface } from "./operation-traits"; import type { OperationTraitInterface } from "./operation-trait"; -export interface OperationInterface extends BaseModel, OperationTraitInterface {} +export type OperationAction = 'send' | 'receive' | 'publish' | 'subscribe'; + +export interface OperationInterface extends BaseModel, OperationTraitInterface { + messages(): MessagesInterface; + traits(): OperationTraitsInterface; +} diff --git a/src/models/schema.ts b/src/models/schema.ts index 5a8af5d6e..c6b4384f1 100644 --- a/src/models/schema.ts +++ b/src/models/schema.ts @@ -1,3 +1,4 @@ import type { BaseModel } from "./base"; +import type { ExtensionsMixinInterface } from "./mixins"; -export interface SchemaInterface extends BaseModel {} +export interface SchemaInterface extends BaseModel, ExtensionsMixinInterface {} diff --git a/src/models/security-scheme.ts b/src/models/security-scheme.ts index d4ab1813e..b551f761c 100644 --- a/src/models/security-scheme.ts +++ b/src/models/security-scheme.ts @@ -1,18 +1,17 @@ -import { BaseModel } from './base'; -import { DescriptionMixinInterface, ExtensionsMixinInterface } from './mixins'; -import { OAuthFlowsInterface } from './oauth-flows'; +import type { BaseModel } from './base'; +import type { OAuthFlowsInterface } from './oauth-flows'; +import type { DescriptionMixinInterface, ExtensionsMixinInterface } from './mixins'; export type SecuritySchemaType = 'userPassword' | 'apiKey' | 'X509' | 'symmetricEncryption' | 'asymmetricEncryption' | 'httpApiKey' | 'http' | 'oauth2' | 'openIdConnect' | 'plain' | 'scramSha256' | 'scramSha512' | 'gssapi'; - export interface SecuritySchemeInterface extends BaseModel, DescriptionMixinInterface, ExtensionsMixinInterface { - id(): string - hasBearerFormat(): boolean; - bearerFormat(): string | undefined; - openIdConnectUrl(): string; - scheme(): string | undefined; - flows(): OAuthFlowsInterface | undefined; - scopes(): string[]; - type(): SecuritySchemaType; - in(): string | undefined; -} \ No newline at end of file + id(): string + hasBearerFormat(): boolean; + bearerFormat(): string | undefined; + openIdConnectUrl(): string; + scheme(): string | undefined; + flows(): OAuthFlowsInterface | undefined; + scopes(): string[]; + type(): SecuritySchemaType; + in(): string | undefined; +} diff --git a/src/models/security-schemes.ts b/src/models/security-schemes.ts index 377919fc1..637acd74d 100644 --- a/src/models/security-schemes.ts +++ b/src/models/security-schemes.ts @@ -1,4 +1,4 @@ -import {Collection} from './collection'; -import { SecuritySchemeInterface } from './security-scheme'; +import type { Collection} from './collection'; +import type { SecuritySchemeInterface } from './security-scheme'; -export interface SecuritySchemesInterface extends Collection {} \ No newline at end of file +export interface SecuritySchemesInterface extends Collection {} diff --git a/src/models/server-variable.ts b/src/models/server-variable.ts index 906b7063a..b729e05e1 100644 --- a/src/models/server-variable.ts +++ b/src/models/server-variable.ts @@ -5,7 +5,7 @@ export interface ServerVariableInterface extends BaseModel, DescriptionMixinInte id(): string; hasDefaultValue(): boolean; defaultValue(): string | undefined; - hasAllowedValue(): boolean; - allowedValue(): Array; + hasAllowedValues(): boolean; + allowedValues(): Array; examples(): Array; } diff --git a/src/models/server.ts b/src/models/server.ts index 5f673b886..ef5f1bff0 100644 --- a/src/models/server.ts +++ b/src/models/server.ts @@ -1,12 +1,14 @@ import type { BaseModel } from "./base"; import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface } from './mixins'; import type { ServerVariablesInterface } from "./server-variables"; +import type { SecuritySchemeInterface } from "./security-scheme"; export interface ServerInterface extends BaseModel, DescriptionMixinInterface, BindingsMixinInterface, ExtensionsMixinInterface { id(): string url(): string; - protocol(): string | undefined; + protocol(): string; protocolVersion(): string; hasProtocolVersion(): boolean; - variables(): ServerVariablesInterface -} \ No newline at end of file + variables(): ServerVariablesInterface; + security(): Array>; +} diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index 65d35296b..338a7043a 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -9,10 +9,7 @@ import { ExtensionsMixin } from './mixins/extensions'; import type { AsyncAPIDocumentInterface, InfoInterface } from "../../models"; import type { ServersInterface } from "models/servers"; -export class AsyncAPIDocument - extends Mixin(BaseModel, ExtensionsMixin) - implements AsyncAPIDocumentInterface { - +export class AsyncAPIDocument extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { version(): string { return this._json.asyncapi; } diff --git a/src/models/v2/correlation-id.ts b/src/models/v2/correlation-id.ts new file mode 100644 index 000000000..127504429 --- /dev/null +++ b/src/models/v2/correlation-id.ts @@ -0,0 +1,17 @@ +import { BaseModel } from "../base"; + +import { Mixin } from '../utils'; +import { DescriptionMixin } from './mixins/description'; +import { ExtensionsMixin } from './mixins/extensions'; + +import type { CorrelationIdInterface } from "../correlation-id"; + +export class CorrelationId extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements CorrelationIdInterface { + hasLocation(): boolean { + return !!this._json.location; + } + + location(): string | undefined { + return this._json.location; + } +} diff --git a/src/models/v2/index.ts b/src/models/v2/index.ts index 2445a397b..d56f9e6e6 100644 --- a/src/models/v2/index.ts +++ b/src/models/v2/index.ts @@ -12,5 +12,5 @@ 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 { OAuthFlow as OAuthFlowV2 } from './oauth-flow'; +export { OAuthFlows as OAuthFlowsV2 } from './oauth-flows'; diff --git a/src/models/v2/info.ts b/src/models/v2/info.ts index 5d53573fc..f88c10ffd 100644 --- a/src/models/v2/info.ts +++ b/src/models/v2/info.ts @@ -8,14 +8,13 @@ import { ExtensionsMixin } from './mixins/extensions'; import { ExternalDocumentation } from './mixins/external-docs'; import { Tags, Tag } from './mixins/tags'; -import type { InfoInterface } from "../../models/info"; -import type { ExternalDocumentationInterface } from "../../models/external-docs"; -import type { TagsInterface } from "../../models/tags"; - -export class Info - extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) - implements InfoInterface { +import type { ContactInterface } from "../contact"; +import type { InfoInterface } from "../info"; +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 { title(): string { return this._json.title; } @@ -24,14 +23,14 @@ export class Info return this._json.version; } - id(): string | undefined { - return this._meta.asyncapi.parsed.id as string; - } - hasId(): boolean { return !!this._meta.asyncapi.parsed.id; } + id(): string | undefined { + return this._meta.asyncapi.parsed.id; + } + hasTermsOfService(): boolean { return !!this._json.termsOfService; } @@ -44,7 +43,7 @@ export class Info return Object.keys(this._json.contact || {}).length > 0; } - contact(): Contact | undefined { + contact(): ContactInterface | undefined { const contact = this._json.contact; return contact && this.createModel(Contact, contact, { pointer: '/info/contact' }); } @@ -53,7 +52,7 @@ export class Info return Object.keys(this._json.license || {}).length > 0; } - license(): License | undefined { + license(): LicenseInterface | undefined { const license = this._json.license; return license && this.createModel(License, license, { pointer: `/info/license` }); } diff --git a/src/models/v2/message-example.ts b/src/models/v2/message-example.ts new file mode 100644 index 000000000..d9fedb931 --- /dev/null +++ b/src/models/v2/message-example.ts @@ -0,0 +1,40 @@ +import { BaseModel } from "../base"; + +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; + +import type { MessageExampleInterface } from "../message-example"; + +export class MessageExample extends Mixin(BaseModel, ExtensionsMixin) implements MessageExampleInterface { + hasName(): boolean { + return !!this._json.name; + } + + name(): string { + return this._json.name; + } + + hasSummary(): boolean { + return !!this._json.summary; + } + + summary(): string { + return this._json.summary; + } + + hasHeaders(): boolean { + return !!this._json.headers; + } + + headers(): Record | undefined { + return this._json.headers; + } + + hasPayload(): boolean { + return !!this._json.payload; + } + + payload(): Record | undefined { + return this._json.payload; + } +} diff --git a/src/models/v2/message-examples.ts b/src/models/v2/message-examples.ts new file mode 100644 index 000000000..062a19abd --- /dev/null +++ b/src/models/v2/message-examples.ts @@ -0,0 +1,14 @@ +import { Collection } from '../collection'; + +import type { MessageExamplesInterface } from '../message-examples'; +import type { MessageExampleInterface } from '../message-example'; + +export class MessageExamples extends Collection implements MessageExamplesInterface { + override get(name: string): MessageExampleInterface | undefined { + return this.collections.find(trait => trait.name() === name); + } + + override has(name: string): boolean { + return this.collections.some(trait => trait.name() === name); + } +} diff --git a/src/models/v2/message-trait.ts b/src/models/v2/message-trait.ts new file mode 100644 index 000000000..a55c9c5c8 --- /dev/null +++ b/src/models/v2/message-trait.ts @@ -0,0 +1,98 @@ +import { BaseModel } from "../base"; +import { CorrelationId } from './correlation-id'; +import { MessageExamples } from './message-examples'; +import { MessageExample } from './message-example'; +import { Schema } from './schema'; + +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 { CorrelationIdInterface } from "../correlation-id"; +import type { MessageExamplesInterface } from "../message-examples"; +import type { MessageTraitInterface } from "../message-trait"; +import type { SchemaInterface } from "../schema"; + +export class MessageTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) implements MessageTraitInterface { + constructor( + private readonly _id: string, + _json: Record, + _meta: ModelMetadata = {} as any + ) { + super(_json, _meta); + } + + id(): string { + return this.messageId() || this._id; + } + + hasMessageId(): boolean { + return !!this._json.messageId; + } + + messageId(): string | undefined { + return this._json.messageId; + } + + hasCorrelationId(): boolean { + return !!this._json.correlationId; + } + + correlationId(): CorrelationIdInterface | undefined { + if (!this._json.correlationId) return undefined; + return this.createModel(CorrelationId, this._json.correlationId, { pointer: `${this._meta.pointer}/correlationId` }); + } + + hasContentType(): boolean { + return !!this._json.contentType; + } + + contentType(): string | undefined { + return this._json.contentType || this._meta.asyncapi?.parsed.defaultContentType; + } + + hasHeaders(): boolean { + return !!this._json.headers; + } + + headers(): SchemaInterface | undefined { + if (!this._json.headers) return undefined; + return this.createModel(Schema, this._json.headers, { pointer: `${this._meta.pointer}/headers` }); + } + + hasName(): boolean { + return !!this._json.name; + } + + name(): string | undefined { + return this._json.name; + } + + hasTitle(): boolean { + return !!this._json.title; + } + + title(): string | undefined { + return this._json.title; + } + + hasSummary(): boolean { + return !!this._json.summary; + } + + summary(): string | undefined { + return this._json.summary; + } + + examples(): MessageExamplesInterface { + return new MessageExamples( + (this._json.examples || []).map((example: any, index: number) => { + return this.createModel(MessageExample, example, { pointer: `${this._meta.pointer}/examples/${index}` }) + }) + ); + } +} diff --git a/src/models/v2/message-traits.ts b/src/models/v2/message-traits.ts new file mode 100644 index 000000000..dcb6074f9 --- /dev/null +++ b/src/models/v2/message-traits.ts @@ -0,0 +1,14 @@ +import { Collection } from '../collection'; + +import type { MessageTraitsInterface } from '../message-traits'; +import type { MessageTraitInterface } from '../message-trait'; + +export class MessageTraits extends Collection implements MessageTraitsInterface { + override get(id: string): MessageTraitInterface | undefined { + return this.collections.find(trait => trait.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(trait => trait.id() === id); + } +} diff --git a/src/models/v2/message.ts b/src/models/v2/message.ts new file mode 100644 index 000000000..eb95a31ad --- /dev/null +++ b/src/models/v2/message.ts @@ -0,0 +1,26 @@ +import { MessageTraits } from "./message-traits"; +import { MessageTrait } from "./message-trait"; +import { Schema } from './schema'; + +import type { MessageInterface } from "../message"; +import type { MessageTraitsInterface } from "../message-traits"; +import type { SchemaInterface } from "../schema"; + +export class Message extends MessageTrait implements MessageInterface { + hasPayload(): boolean { + return !!this._json.payload; + } + + payload(): SchemaInterface | undefined { + if (!this._json.payload) return undefined; + return this.createModel(Schema, this._json.payload, { pointer: `${this._meta.pointer}/payload` }); + } + + traits(): MessageTraitsInterface { + return new MessageTraits( + (this._json.traits || []).map((trait: any, index: number) => { + return this.createModel(MessageTrait, trait, { pointer: `${this._meta.pointer}/traits/${index}` }) + }) + ); + } +} diff --git a/src/models/v2/messages.ts b/src/models/v2/messages.ts new file mode 100644 index 000000000..4bb9e305f --- /dev/null +++ b/src/models/v2/messages.ts @@ -0,0 +1,14 @@ +import { Collection } from '../collection'; + +import type { MessagesInterface } from '../messages'; +import type { MessageInterface } from '../message'; + +export class Messages extends Collection implements MessagesInterface { + override get(id: string): MessageInterface | undefined { + return this.collections.find(message => message.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(message => message.id() === id); + } +} diff --git a/src/models/v2/mixins/extensions.ts b/src/models/v2/mixins/extensions.ts index b95175fa5..727bb6598 100644 --- a/src/models/v2/mixins/extensions.ts +++ b/src/models/v2/mixins/extensions.ts @@ -32,10 +32,12 @@ export class Extension extends BaseModel implements ExtensionInterface { 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.id() === name); }; override has(name: string): boolean { + name = name.startsWith('x-') ? name : `x-${name}`; return this.collections.some(ext => ext.id() === name); }; } diff --git a/src/models/v2/oauth-flow.ts b/src/models/v2/oauth-flow.ts index 2891357f9..daeade1cb 100644 --- a/src/models/v2/oauth-flow.ts +++ b/src/models/v2/oauth-flow.ts @@ -1,23 +1,28 @@ -import { OAuthFlowInterface } from '../oauth-flow'; import { BaseModel } from '../base'; + import { Mixin } from '../utils'; import { ExtensionsMixin } from './mixins/extensions'; +import type { OAuthFlowInterface } from '../oauth-flow'; + export class OAuthFlow extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowInterface { - authorizationUrl(): string | undefined { - return this._json.authorizationUrl; - } - hasRefreshUrl(): boolean { - return !!this._json.refreshUrl; - } - refreshUrl(): string | undefined { - return this._json.refreshUrl; - } - scopes(): Record | undefined { - return this._json.scopes; - } - tokenUrl(): string | undefined { - return this._json.tokenUrl; - } + authorizationUrl(): string | undefined { + return this._json.authorizationUrl; + } + + hasRefreshUrl(): boolean { + return !!this._json.refreshUrl; + } + + refreshUrl(): string | undefined { + return this._json.refreshUrl; + } + + scopes(): Record | undefined { + return this._json.scopes; + } + tokenUrl(): string | undefined { + return this._json.tokenUrl; + } } \ No newline at end of file diff --git a/src/models/v2/oauth-flows.ts b/src/models/v2/oauth-flows.ts index 51399a416..d83f291ed 100644 --- a/src/models/v2/oauth-flows.ts +++ b/src/models/v2/oauth-flows.ts @@ -1,39 +1,46 @@ -import { OAuthFlowsInterface } from '../oauth-flows'; import { BaseModel } from '../base'; + +import { OAuthFlow } from './oauth-flow'; import { Mixin } from '../utils'; import { ExtensionsMixin } from './mixins/extensions'; -import { OAuthFlowInterface } from 'models/oauth-flow'; -import { OAuthFlow } from './oauth-flow'; - -export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowsInterface { - hasAuthorizationCode(): boolean { - return !!this._json.authorizationCode; - } - authorizationCode(): OAuthFlowInterface | undefined { - if (!this._json.authorizationCode) return undefined; - return new OAuthFlow(this._json.authorizationCode); - } - hasClientCredentials(): boolean { - return !!this._json.clientCredentials; - } - clientCredentials(): OAuthFlowInterface | undefined { - if (!this._json.clientCredentials) return undefined; - return new OAuthFlow(this._json.clientCredentials); - } - hasImplicit(): boolean { - return !!this._json.implicit; - } - implicit(): OAuthFlowInterface | undefined { - if (!this._json.implicit) return undefined; - return new OAuthFlow(this._json.implicit); - } - hasPassword(): boolean { - return !!this._json.password; - } - password(): OAuthFlowInterface | undefined { - if (!this._json.password) return undefined; - return new OAuthFlow(this._json.password); - } +import type { OAuthFlowsInterface } from '../oauth-flows'; +import type { OAuthFlowInterface } from '../oauth-flow'; +export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowsInterface { + hasAuthorizationCode(): boolean { + return !!this._json.authorizationCode; + } + + authorizationCode(): OAuthFlowInterface | undefined { + if (!this._json.authorizationCode) return undefined; + return new OAuthFlow(this._json.authorizationCode); + } + + hasClientCredentials(): boolean { + return !!this._json.clientCredentials; + } + + clientCredentials(): OAuthFlowInterface | undefined { + if (!this._json.clientCredentials) return undefined; + return new OAuthFlow(this._json.clientCredentials); + } + + hasImplicit(): boolean { + return !!this._json.implicit; + } + + implicit(): OAuthFlowInterface | undefined { + if (!this._json.implicit) return undefined; + return new OAuthFlow(this._json.implicit); + } + + hasPassword(): boolean { + return !!this._json.password; + } + + password(): OAuthFlowInterface | undefined { + if (!this._json.password) return undefined; + return new OAuthFlow(this._json.password); + } } \ No newline at end of file diff --git a/src/models/v2/operation-trait.ts b/src/models/v2/operation-trait.ts new file mode 100644 index 000000000..aa3ccea40 --- /dev/null +++ b/src/models/v2/operation-trait.ts @@ -0,0 +1,62 @@ +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 type { OperationAction } from "../operation"; +import type { OperationTraitInterface } from "../operation-trait"; +import type { SecuritySchemeInterface } from "../security-scheme"; + +export class OperationTrait extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin, ExternalDocumentationMixin, TagsMixin) implements OperationTraitInterface { + constructor( + private readonly _id: string, + _json: Record, + public readonly _meta: ModelMetadata & { action: OperationAction } = {} as any, + ) { + super(_json, _meta); + } + + id(): string { + return this.operationId() || this._id; + } + + action(): OperationAction { + return this._meta.action; + } + + hasOperationId(): boolean { + return !!this._json.operationId; + } + + operationId(): string | undefined { + return this._json.operationId; + } + + hasSummary(): boolean { + return !!this._json.summary; + } + + summary(): string | undefined { + return this._json.summary; + } + + security(): Array> { + const securitySchemes = this._meta?.asyncapi?.parsed.components.securitySchemes || {}; + return (this._json.security || []).map((requirement: any) => { + const requirements: Record = {}; + Object.entries(requirement).forEach(([security, scopes]) => { + requirements[security] = { + schema: this.createModel(SecurityScheme, securitySchemes[security], { id: security, pointer: `/components/securitySchemes/${security}` }), + scopes: scopes as Array, + } + }); + return requirements; + }) + } +} diff --git a/src/models/v2/operation-traits.ts b/src/models/v2/operation-traits.ts new file mode 100644 index 000000000..95d5f9e32 --- /dev/null +++ b/src/models/v2/operation-traits.ts @@ -0,0 +1,14 @@ +import { Collection } from '../collection'; + +import type { OperationTraitsInterface } from '../operation-traits'; +import type { OperationTraitInterface } from '../operation-trait'; + +export class OperationTraits extends Collection implements OperationTraitsInterface { + override get(id: string): OperationTraitInterface | undefined { + return this.collections.find(trait => trait.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(trait => trait.id() === id); + } +} diff --git a/src/models/v2/operation.ts b/src/models/v2/operation.ts new file mode 100644 index 000000000..4ac9ecbd3 --- /dev/null +++ b/src/models/v2/operation.ts @@ -0,0 +1,35 @@ +import { Message } from "./message"; +import { Messages } from "./messages"; +import { OperationTraits } from "./operation-traits"; +import { OperationTrait } from "./operation-trait"; + +import type { MessagesInterface } from "../messages"; +import type { OperationInterface } from "../operation"; +import type { OperationTraitsInterface } from "../operation-traits"; + +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]; + } + + return new Messages( + messages.map((message: any, index: number) => { + return this.createModel(Message, message, { pointer: `${this._meta.pointer}/message${isOneOf ? `/oneOf/${index}` : ''}` }) + }) + ); + } + + traits(): OperationTraitsInterface { + return new OperationTraits( + (this._json.traits || []).map((trait: any, index: number) => { + return this.createModel(OperationTrait, trait, { pointer: `${this._meta.pointer}/traits/${index}` }) + }) + ); + } +} diff --git a/src/models/v2/operations.ts b/src/models/v2/operations.ts new file mode 100644 index 000000000..d67d0608b --- /dev/null +++ b/src/models/v2/operations.ts @@ -0,0 +1,14 @@ +import { Collection } from '../collection'; + +import type { OperationsInterface } from '../operations'; +import type { OperationInterface } from '../operation'; + +export class Operations extends Collection implements OperationsInterface { + override get(id: string): OperationInterface | undefined { + return this.collections.find(operation => operation.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(operation => operation.id() === id); + } +} diff --git a/src/models/v2/schema.ts b/src/models/v2/schema.ts new file mode 100644 index 000000000..24eb9ea9a --- /dev/null +++ b/src/models/v2/schema.ts @@ -0,0 +1,8 @@ +import { BaseModel } from "../base"; + +import { Mixin } from '../utils'; +import { ExtensionsMixin } from './mixins/extensions'; + +import type { SchemaInterface } from "../schema"; + +export class Schema extends Mixin(BaseModel, ExtensionsMixin) implements SchemaInterface {} diff --git a/src/models/v2/security-scheme.ts b/src/models/v2/security-scheme.ts index ce2785970..bedeb610c 100644 --- a/src/models/v2/security-scheme.ts +++ b/src/models/v2/security-scheme.ts @@ -1,55 +1,57 @@ -import { BaseModel, ModelMetadata } from '../base'; +import { BaseModel } from '../base'; + +import { OAuthFlows } from './oauth-flows'; import { Mixin } from '../utils'; import { DescriptionMixin } from './mixins/description'; import { ExtensionsMixin } from './mixins/extensions'; -import { SecuritySchemaType, SecuritySchemeInterface } from '../security-scheme'; -import { OAuthFlowsInterface } from 'models/oauth-flows'; -import { OAuthFlows } from './oauth-flows'; + +import type { ModelMetadata } from '../base'; +import type { SecuritySchemaType, SecuritySchemeInterface } from '../security-scheme'; +import type { OAuthFlowsInterface } from '../oauth-flows'; export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements SecuritySchemeInterface { - constructor( - private readonly _id: string, - _json: Record, - _meta: ModelMetadata = {} as any - ) { - super(_json, _meta); - } - - id(): string { - return this._id; - } - - hasBearerFormat(): boolean { - return !!this._json.bearerFormat; - } - - bearerFormat(): string | undefined { - return this._json.bearerFormat; - } - - openIdConnectUrl(): string { - return this._json.openIdConnectUrl; - } - - scheme(): string | undefined { - return this._json.scheme - } - - flows(): OAuthFlowsInterface | undefined { - if(!this._json.flows) return undefined; - return new OAuthFlows(this._json.flows); - } - - scopes(): string[] { - return this._json.scopes; - } - - type(): SecuritySchemaType { - return this._json.type; - } - - in(): string | undefined { - return this._json.in; - } - -} \ No newline at end of file + constructor( + private readonly _id: string, + _json: Record, + _meta: ModelMetadata = {} as any + ) { + super(_json, _meta); + } + + id(): string { + return this._id; + } + + hasBearerFormat(): boolean { + return !!this._json.bearerFormat; + } + + bearerFormat(): string | undefined { + return this._json.bearerFormat; + } + + openIdConnectUrl(): string { + return this._json.openIdConnectUrl; + } + + scheme(): string | undefined { + return this._json.scheme + } + + flows(): OAuthFlowsInterface | undefined { + if(!this._json.flows) return undefined; + return new OAuthFlows(this._json.flows); + } + + scopes(): string[] { + return this._json.scopes; + } + + type(): SecuritySchemaType { + return this._json.type; + } + + in(): string | undefined { + return this._json.in; + } +} diff --git a/src/models/v2/security-schemes.ts b/src/models/v2/security-schemes.ts index 500ded280..985a2d941 100644 --- a/src/models/v2/security-schemes.ts +++ b/src/models/v2/security-schemes.ts @@ -1,12 +1,14 @@ -import { SecuritySchemesInterface } from '../security-schemes'; import { Collection } from '../collection'; -import { SecuritySchemeInterface } from '../security-scheme'; + +import type { SecuritySchemesInterface } from '../security-schemes'; +import type { SecuritySchemeInterface } from '../security-scheme'; export class SecuritySchemes extends Collection implements SecuritySchemesInterface { - get(id: string): SecuritySchemeInterface | undefined { - return this.collections.find(securityScheme => securityScheme.id() === id); - } - has(id: string): boolean { - return this.collections.some(securityScheme => securityScheme.id() === id); - } -} \ No newline at end of file + override get(id: string): SecuritySchemeInterface | undefined { + return this.collections.find(securityScheme => securityScheme.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(securityScheme => securityScheme.id() === id); + } +} diff --git a/src/models/v2/server-variable.ts b/src/models/v2/server-variable.ts index 9325a10fc..e471973e7 100644 --- a/src/models/v2/server-variable.ts +++ b/src/models/v2/server-variable.ts @@ -1,35 +1,42 @@ -import { BaseModel, ModelMetadata } from '../base'; +import { BaseModel } from '../base'; + import { Mixin } from '../utils'; -import { ServerVariableInterface } from '../server-variable'; import { DescriptionMixin } from './mixins/description'; import { ExtensionsMixin } from './mixins/extensions'; +import type { ModelMetadata } from '../base'; +import type { ServerVariableInterface } from '../server-variable'; export class ServerVariable extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements ServerVariableInterface { - constructor( - private readonly _id: string, - _json: Record, - _meta: ModelMetadata = {} as any - ){ - super(_json, _meta); - } - id(): string { - return this._id; - } - hasDefaultValue(): boolean { - return !!this._json.default - } - defaultValue(): string | undefined { - return this._json.default; - } - hasAllowedValue(): boolean { - return !!this._json.enum; - } - allowedValue(): any[] { - return this._json.enum; - } - examples(): string[] { - return this._json.examples - } + constructor( + private readonly _id: string, + _json: Record, + _meta: ModelMetadata = {} as any + ) { + super(_json, _meta); + } + + id(): string { + return this._id; + } + + hasDefaultValue(): boolean { + return !!this._json.default + } + + defaultValue(): string | undefined { + return this._json.default; + } + + hasAllowedValues(): boolean { + return !!this._json.enum; + } -} \ No newline at end of file + allowedValues(): Array { + return this._json.enum; + } + + examples(): Array { + return this._json.examples + } +} diff --git a/src/models/v2/server-variables.ts b/src/models/v2/server-variables.ts index b61cb55d0..9edc795d5 100644 --- a/src/models/v2/server-variables.ts +++ b/src/models/v2/server-variables.ts @@ -1,15 +1,14 @@ -import { ServerVariablesInterface } from '../server-variables'; import { Collection } from '../collection'; -import { ServerVariableInterface } from '../server-variable'; -export class ServerVariables extends Collection implements ServerVariablesInterface { - get(id: string): ServerVariableInterface | undefined { - return this.collections.find(serverVariable => serverVariable.id() === id); - } +import type { ServerVariablesInterface } from '../server-variables'; +import type { ServerVariableInterface } from '../server-variable'; - has(id: string): boolean { - return this.collections.some(serverVariable => serverVariable.id() === id); - } +export class ServerVariables extends Collection implements ServerVariablesInterface { + override get(id: string): ServerVariableInterface | undefined { + return this.collections.find(serverVariable => serverVariable.id() === id); + } + override has(id: string): boolean { + return this.collections.some(serverVariable => serverVariable.id() === id); + } } - diff --git a/src/models/v2/server.ts b/src/models/v2/server.ts index 165e82a3d..d19154582 100644 --- a/src/models/v2/server.ts +++ b/src/models/v2/server.ts @@ -1,4 +1,7 @@ import { BaseModel } from '../base'; +import { SecurityScheme } from './security-scheme'; +import { ServerVariables } from './server-variables'; +import { ServerVariable } from './server-variable'; import { Mixin } from '../utils'; import { BindingsMixin } from './mixins/bindings'; @@ -7,9 +10,8 @@ import { ExtensionsMixin } from './mixins/extensions'; import type { ModelMetadata } from "../base"; import type { ServerInterface } from '../server'; -import { ServerVariablesInterface } from '../server-variables'; -import { ServerVariables } from './server-variables'; -import { ServerVariable } from './server-variable'; +import type { ServerVariablesInterface } from '../server-variables'; +import type { SecuritySchemeInterface } from '../security-scheme'; export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin) implements ServerInterface { constructor( @@ -28,7 +30,7 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex return this._json.url; } - protocol(): string | undefined { + protocol(): string { return this._json.protocol; } @@ -42,16 +44,26 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex variables(): ServerVariablesInterface { return new ServerVariables( - Object.entries( - this._json.variables - ).map( - ([serverVariableName, serverVariable]) => this.createModel( - ServerVariable, serverVariable, { + Object.entries(this._json.variables || {}).map(([serverVariableName, serverVariable]) => { + return this.createModel(ServerVariable, serverVariable, { id: serverVariableName, pointer: `${this._meta.pointer}/variables/${serverVariableName}` - } - ) - )) + }) + }) + ); } -} \ No newline at end of file + security(): Array> { + const securitySchemes = this._meta?.asyncapi?.parsed.components.securitySchemes || {}; + return (this._json.security || []).map((requirement: any) => { + const requirements: Record = {}; + Object.entries(requirement).forEach(([security, scopes]) => { + requirements[security] = { + schema: this.createModel(SecurityScheme, securitySchemes[security], { id: security, pointer: `/components/securitySchemes/${security}` }), + scopes: scopes as Array, + } + }); + return requirements; + }) + } +} diff --git a/src/models/v3/oauth-flows.ts b/src/models/v3/oauth-flows.ts index 592f49ab7..6abab0eb8 100644 --- a/src/models/v3/oauth-flows.ts +++ b/src/models/v3/oauth-flows.ts @@ -3,7 +3,7 @@ import { OAuthFlowsInterface } from '../oauth-flows'; import { BaseModel } from '../base'; import { Mixin } from '../utils'; import { ExtensionsMixin } from './mixins/extensions'; -import { OAuthFlowInterface } from 'models/oauth-flow'; +import { OAuthFlowInterface } from '../oauth-flow'; import { OAuthFlow } from './oauth-flow'; export class OAuthFlows extends Mixin(BaseModel, ExtensionsMixin) implements OAuthFlowsInterface { diff --git a/src/models/v3/security-scheme.ts b/src/models/v3/security-scheme.ts index ce2785970..eecbaa843 100644 --- a/src/models/v3/security-scheme.ts +++ b/src/models/v3/security-scheme.ts @@ -3,7 +3,7 @@ import { Mixin } from '../utils'; import { DescriptionMixin } from './mixins/description'; import { ExtensionsMixin } from './mixins/extensions'; import { SecuritySchemaType, SecuritySchemeInterface } from '../security-scheme'; -import { OAuthFlowsInterface } from 'models/oauth-flows'; +import { OAuthFlowsInterface } from '../oauth-flows'; import { OAuthFlows } from './oauth-flows'; export class SecurityScheme extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements SecuritySchemeInterface { diff --git a/src/models/v3/server-variable.ts b/src/models/v3/server-variable.ts index 1f931e83f..e471973e7 100644 --- a/src/models/v3/server-variable.ts +++ b/src/models/v3/server-variable.ts @@ -1,28 +1,42 @@ import { BaseModel } from '../base'; + import { Mixin } from '../utils'; -import { ServerVariableInterface } from '../server-variable'; import { DescriptionMixin } from './mixins/description'; import { ExtensionsMixin } from './mixins/extensions'; +import type { ModelMetadata } from '../base'; +import type { ServerVariableInterface } from '../server-variable'; export class ServerVariable extends Mixin(BaseModel, DescriptionMixin, ExtensionsMixin) implements ServerVariableInterface { - id(): string { - throw new Error('Method not implemented.'); - } - hasDefaultValue(): boolean { - return !!this._json.default - } - defaultValue(): string | undefined { - return this._json.default; - } - hasAllowedValue(): boolean { - return !!this._json.enum - } - allowedValue(): any[] { - return this._json.enum - } - examples(): string[] { - return this._json.examples - } + constructor( + private readonly _id: string, + _json: Record, + _meta: ModelMetadata = {} as any + ) { + super(_json, _meta); + } + + id(): string { + return this._id; + } + + hasDefaultValue(): boolean { + return !!this._json.default + } + + defaultValue(): string | undefined { + return this._json.default; + } + + hasAllowedValues(): boolean { + return !!this._json.enum; + } -} \ No newline at end of file + allowedValues(): Array { + return this._json.enum; + } + + examples(): Array { + return this._json.examples + } +} diff --git a/src/models/v3/server.ts b/src/models/v3/server.ts index ce67bb8bb..19ac7aacc 100644 --- a/src/models/v3/server.ts +++ b/src/models/v3/server.ts @@ -1,16 +1,16 @@ import { BaseModel } from '../base'; +import { SecurityScheme } from './security-scheme'; +import { ServerVariable } from './server-variable'; +import { ServerVariables } from './server-variables'; import { Mixin } from '../utils'; import { BindingsMixin } from './mixins/bindings'; import { DescriptionMixin } from './mixins/description'; import { ExtensionsMixin } from './mixins/extensions'; -import { ServerVariable } from './server-variable'; -import { ServerVariables } from './server-variables'; -import { ServerVariablesInterface } from '../server-variables'; import type { ModelMetadata } from "../base"; import type { ServerInterface } from '../server'; - +import type { SecuritySchemeInterface } from '../security-scheme'; export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, ExtensionsMixin) implements ServerInterface { constructor( @@ -29,7 +29,7 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex return this._json.url; } - protocol(): string | undefined { + protocol(): string { return this._json.protocol; } @@ -41,7 +41,7 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex return this._json.protocolVersion; } - variables(): ServerVariablesInterface { + variables(): ServerVariables { return new ServerVariables( Object.entries( this._json.variables @@ -55,4 +55,17 @@ export class Server extends Mixin(BaseModel, BindingsMixin, DescriptionMixin, Ex )) } -} \ No newline at end of file + security(): Array> { + const securitySchemes = this._meta?.asyncapi?.parsed.components.securitySchemes || {}; + return (this._json.security || []).map((requirement: any) => { + const requirements: Record = {}; + Object.entries(requirement).forEach(([security, scopes]) => { + requirements[security] = { + schema: this.createModel(SecurityScheme, securitySchemes[security], { id: security, pointer: `/components/securitySchemes/${security}` }), + scopes: scopes as Array, + } + }); + return requirements; + }) + } +} diff --git a/test/models/v2/correlation-id.spec.ts b/test/models/v2/correlation-id.spec.ts new file mode 100644 index 000000000..18b4e3918 --- /dev/null +++ b/test/models/v2/correlation-id.spec.ts @@ -0,0 +1,41 @@ +import { CorrelationId } from '../../../src/models/v2/correlation-id'; + +import { + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; + +describe('CorrelationId model', function() { + describe('.hasLocation()', function() { + it('should return true when there is a value', function() { + const doc = { location: "..." }; + const d = new CorrelationId(doc); + expect(d.hasLocation()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new CorrelationId(doc); + expect(d.hasLocation()).toEqual(false); + }); + }); + + describe('.location()', function() { + it('should return the value', function() { + const doc = { 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 d = new CorrelationId(doc); + expect(d.location()).toBeUndefined(); + }); + }); + + describe('mixins inheritance', function() { + assertDescriptionMixinInheritance(CorrelationId); + assertExtensionsMixinInheritance(CorrelationId); + }); +}); diff --git a/test/models/v2/info.spec.ts b/test/models/v2/info.spec.ts index 708917cf6..042f60c2e 100644 --- a/test/models/v2/info.spec.ts +++ b/test/models/v2/info.spec.ts @@ -31,14 +31,14 @@ describe('Info model', 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, parent: null, pointer: '/info' }); + const d = new Info({}, { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.hasId()).toEqual(false); }); }); @@ -47,14 +47,14 @@ describe('Info model', 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, parent: null, pointer: '/info' }); + const d = new Info({}, { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.id()).toEqual(undefined); }); }); @@ -147,21 +147,21 @@ describe('Info model', 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, parent: null, pointer: '/info' }); + const d = new Info({}, { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.hasExternalDocs()).toEqual(false); }); }); @@ -170,7 +170,7 @@ describe('Info model', 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.externalDocs()).toBeInstanceOf(ExternalDocumentation); expect(d.externalDocs()!.json()).toEqual(doc.externalDocs); }); @@ -178,14 +178,14 @@ describe('Info model', function() { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.externalDocs()).toEqual(undefined); }); }); @@ -195,7 +195,7 @@ describe('Info model', function() { const tags = [{ name: 'one' }, { name: 'two' }]; const doc = { asyncapi: '2.0.0', tags }; const asyncapi = createDetailedAsyncAPI(doc, doc); - const d = new Info({}, { asyncapi, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.tags()).toBeInstanceOf(Tags); expect(d.tags().length).toEqual(2); expect(d.tags().all()[0]).toBeInstanceOf(Tag); @@ -205,7 +205,7 @@ 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, parent: null, pointer: '/info' }); + const d = new Info({}, { asyncapi, pointer: '/info' }); expect(d.tags()).toBeInstanceOf(Tags); expect(d.tags().all()).toEqual([]); }); diff --git a/test/models/v2/message-example.spec.ts b/test/models/v2/message-example.spec.ts new file mode 100644 index 000000000..bc41f171b --- /dev/null +++ b/test/models/v2/message-example.spec.ts @@ -0,0 +1,123 @@ +import { MessageExample } from '../../../src/models/v2/message-example'; + +import { + assertExtensionsMixinInheritance, +} from './mixins/inheritance'; + +describe('MessageExample model', function() { + describe('.hasName()', function() { + it('should return true when there is a value', function() { + const doc = { name: "..." }; + const d = new MessageExample(doc); + expect(d.hasName()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.hasName()).toEqual(false); + }); + }); + + describe('.name()', function() { + it('should return the value', function() { + const doc = { name: "..." }; + const d = new MessageExample(doc); + expect(d.name()).toEqual(doc.name); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.name()).toBeUndefined(); + }); + }); + + describe('.hasSummary()', function() { + it('should return true when there is a value', function() { + const doc = { summary: "..." }; + const d = new MessageExample(doc); + expect(d.hasSummary()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.hasSummary()).toEqual(false); + }); + }); + + describe('.summary()', function() { + it('should return the value', function() { + const doc = { summary: "..." }; + const d = new MessageExample(doc); + expect(d.summary()).toEqual(doc.summary); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.summary()).toBeUndefined(); + }); + }); + + describe('.hasHeaders()', function() { + it('should return true when there is a value', function() { + const doc = { headers: {} }; + const d = new MessageExample(doc); + expect(d.hasHeaders()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.hasSummary()).toEqual(false); + }); + }); + + describe('.headers()', function() { + it('should return the value', function() { + const doc = { headers: {} }; + const d = new MessageExample(doc); + expect(d.headers()).toEqual(doc.headers); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.headers()).toBeUndefined(); + }); + }); + + describe('.hasPayload()', function() { + it('should return true when there is a value', function() { + const doc = { payload: {} }; + const d = new MessageExample(doc); + expect(d.hasPayload()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.hasPayload()).toEqual(false); + }); + }); + + describe('.payload()', function() { + it('should return the value', function() { + const doc = { payload: {} }; + const d = new MessageExample(doc); + expect(d.payload()).toEqual(doc.payload); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageExample(doc); + expect(d.payload()).toBeUndefined(); + }); + }); + + describe('mixins inheritance', function() { + assertExtensionsMixinInheritance(MessageExample); + }); +}); diff --git a/test/models/v2/message-examples.spec.ts b/test/models/v2/message-examples.spec.ts new file mode 100644 index 000000000..f04c8a3ef --- /dev/null +++ b/test/models/v2/message-examples.spec.ts @@ -0,0 +1,45 @@ +import { MessageExamples } from '../../../src/models/v2/message-examples'; +import { MessageExample } from '../../../src/models/v2/message-example'; + +const messageExample = { + name: 'test', +}; +const messageExampleItem = new MessageExample(messageExample); + +describe('MessageExamples model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new MessageExamples([]); + expect(servers.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function () { + const servers = new MessageExamples([messageExampleItem]); + expect(servers.isEmpty()).toEqual(false); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Message Example if it is present', function () { + const servers = new MessageExamples([messageExampleItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Message Example is missing', function () { + const servers = new MessageExamples([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said name is available', function () { + const servers = new MessageExamples([messageExampleItem]); + expect(servers.has('test')).toEqual(true); + }) + + it('should return false if the Message Example name is missing', function () { + const servers = new MessageExamples([messageExampleItem]); + expect(servers.has('anotherName')).toEqual(false); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/message-trait.spec.ts b/test/models/v2/message-trait.spec.ts new file mode 100644 index 000000000..4ad51f934 --- /dev/null +++ b/test/models/v2/message-trait.spec.ts @@ -0,0 +1,256 @@ +import { CorrelationId } from '../../../src/models/v2/correlation-id'; +import { MessageExamples } from '../../../src/models/v2/message-examples'; +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'; + +describe('MessageTrait model', function() { + describe('.id()', function() { + it('should return id of model', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.id()).toEqual('trait'); + }); + + it('should reuse messageId', function() { + const doc = { messageId: '...' }; + const d = new MessageTrait('trait', doc); + expect(d.id()).toEqual(doc.messageId); + }); + }); + + describe('.hasMessageId()', function() { + it('should return true when there is a value', function() { + const doc = { messageId: '...' }; + const d = new MessageTrait('trait', doc); + expect(d.hasMessageId()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasMessageId()).toEqual(false); + }); + }); + + describe('.messageId()', function() { + it('should return the value', function() { + const doc = { messageId: '...' }; + const d = new MessageTrait('trait', doc); + expect(d.messageId()).toEqual(doc.messageId); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.messageId()).toBeUndefined(); + }); + }); + + describe('.hasCorrelationId()', function() { + it('should return true when there is a value', function() { + const doc = { correlationId: {} }; + const d = new MessageTrait('trait', doc); + expect(d.hasCorrelationId()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasCorrelationId()).toEqual(false); + }); + }); + + describe('.correlationId()', function() { + it('should return the value', function() { + const doc = { correlationId: {} }; + const d = new MessageTrait('trait', doc); + expect(d.correlationId()).toBeInstanceOf(CorrelationId); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.correlationId()).toBeUndefined(); + }); + }); + + describe('.hasContentType()', function() { + it('should return true when there is a value', function() { + const doc = { contentType: '...' }; + const d = new MessageTrait('trait', doc); + expect(d.hasContentType()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasContentType()).toEqual(false); + }); + }); + + describe('.contentType()', function() { + it('should return the value', function() { + const doc = { contentType: '...' }; + const d = new MessageTrait('trait', doc); + expect(d.contentType()).toEqual(doc.contentType); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.contentType()).toBeUndefined(); + }); + + it('should reuse defaultContentType value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc, { asyncapi: { parsed: { defaultContentType: '...' } } } as any); + expect(d.contentType()).toEqual('...'); + }); + }); + + describe('.hasHeaders()', function() { + it('should return true when there is a value', function() { + const doc = { headers: {} }; + const d = new MessageTrait('trait', doc); + expect(d.hasHeaders()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasHeaders()).toEqual(false); + }); + }); + + describe('.headers()', function() { + it('should return the value', function() { + const doc = { headers: {} }; + const d = new MessageTrait('trait', doc); + expect(d.headers()).toBeInstanceOf(Schema); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.headers()).toBeUndefined(); + }); + }); + + describe('.hasName()', function() { + it('should return true when there is a value', function() { + const doc = { name: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.hasName()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasName()).toEqual(false); + }); + }); + + describe('.name()', function() { + it('should return the value', function() { + const doc = { name: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.name()).toEqual(doc.name); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.name()).toBeUndefined(); + }); + }); + + describe('.hasTitle()', function() { + it('should return true when there is a value', function() { + const doc = { title: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.hasTitle()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasTitle()).toEqual(false); + }); + }); + + describe('.title()', function() { + it('should return the value', function() { + const doc = { title: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.title()).toEqual(doc.title); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.title()).toBeUndefined(); + }); + }); + + describe('.hasSummary()', function() { + it('should return true when there is a value', function() { + const doc = { summary: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.hasSummary()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.hasSummary()).toEqual(false); + }); + }); + + describe('.summary()', function() { + it('should return the value', function() { + const doc = { summary: "..." }; + const d = new MessageTrait('trait', doc); + expect(d.summary()).toEqual(doc.summary); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.summary()).toBeUndefined(); + }); + }); + + describe('.examples()', function() { + it('should return collection of examples', function() { + const doc = { examples: [ { name: '...' } ] }; + const d = new MessageTrait('trait', doc); + expect(d.examples()).toBeInstanceOf(MessageExamples); + expect(d.examples().all()).toHaveLength(1); + expect(d.examples().all()[0]).toBeInstanceOf(MessageExample); + }); + + it('should return collection of examples when value is undefined', function() { + const doc = {}; + const d = new MessageTrait('trait', doc); + expect(d.examples()).toBeInstanceOf(MessageExamples); + expect(d.examples().all()).toHaveLength(0); + }); + }); + + describe('mixins inheritance', function() { + assertBindingsMixinInheritance(MessageTrait); + assertDescriptionMixinInheritance(MessageTrait); + assertExtensionsMixinInheritance(MessageTrait); + assertExternalDocumentationMixinInheritance(MessageTrait); + assertTagsMixinInheritance(MessageTrait); + }); +}); diff --git a/test/models/v2/message-traits.spec.ts b/test/models/v2/message-traits.spec.ts new file mode 100644 index 000000000..9343c715d --- /dev/null +++ b/test/models/v2/message-traits.spec.ts @@ -0,0 +1,45 @@ +import { MessageTraits } from '../../../src/models/v2/message-traits'; +import { MessageTrait } from '../../../src/models/v2/message-trait'; + +const messageTrait = { + messageId: 'test', +}; +const messageTraitItem = new MessageTrait('test', messageTrait); + +describe('MessageTraits model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new MessageTraits([]); + expect(servers.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function () { + const servers = new MessageTraits([messageTraitItem]); + expect(servers.isEmpty()).toEqual(false); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Message Trait if it is present', function () { + const servers = new MessageTraits([messageTraitItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Message Trait is missing', function () { + const servers = new MessageTraits([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said id is available', function () { + const servers = new MessageTraits([messageTraitItem]); + expect(servers.has('test')).toEqual(true); + }) + + it('should return false if the Message Trait id is missing', function () { + const servers = new MessageTraits([messageTraitItem]); + expect(servers.has('anotherId')).toEqual(false); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/message.spec.ts b/test/models/v2/message.spec.ts new file mode 100644 index 000000000..625191eca --- /dev/null +++ b/test/models/v2/message.spec.ts @@ -0,0 +1,81 @@ +import { Message } from '../../../src/models/v2/message'; +import { MessageTraits } from '../../../src/models/v2/message-traits'; +import { MessageTrait } from '../../../src/models/v2/message-trait'; +import { Schema } from '../../../src/models/v2/schema'; + +import { + assertBindingsMixinInheritance, + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, + assertExternalDocumentationMixinInheritance, + assertTagsMixinInheritance, +} from './mixins/inheritance'; + +describe('Message model', function() { + describe('.id()', function() { + it('should return id of model', function() { + const doc = {}; + const d = new Message('message', doc); + expect(d.id()).toEqual('message'); + }); + + it('should reuse messageId', function() { + const doc = { messageId: '...' }; + const d = new Message('message', doc); + expect(d.id()).toEqual(doc.messageId); + }); + }); + + describe('.hasPayload()', function() { + it('should return true when there is a value', function() { + const doc = { payload: {} }; + const d = new Message('message', doc); + expect(d.hasPayload()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new Message('message', doc); + expect(d.hasPayload()).toEqual(false); + }); + }); + + describe('.payload()', function() { + it('should return the value', function() { + const doc = { payload: {} }; + const d = new Message('message', doc); + expect(d.payload()).toBeInstanceOf(Schema); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new Message('message', doc); + expect(d.payload()).toBeUndefined(); + }); + }); + + describe('.traits()', function() { + it('should return collection of traits', function() { + const doc = { traits: [ { messageId: '...' } ] }; + const d = new Message('message', doc); + expect(d.traits()).toBeInstanceOf(MessageTraits); + expect(d.traits().all()).toHaveLength(1); + expect(d.traits().all()[0]).toBeInstanceOf(MessageTrait); + }); + + it('should return collection of traits when value is undefined', function() { + const doc = {}; + const d = new Message('message', doc); + expect(d.traits()).toBeInstanceOf(MessageTraits); + expect(d.traits().all()).toHaveLength(0); + }); + }); + + describe('mixins inheritance', function() { + assertBindingsMixinInheritance(MessageTrait); + assertDescriptionMixinInheritance(MessageTrait); + assertExtensionsMixinInheritance(MessageTrait); + assertExternalDocumentationMixinInheritance(MessageTrait); + assertTagsMixinInheritance(MessageTrait); + }); +}); diff --git a/test/models/v2/messages.spec.ts b/test/models/v2/messages.spec.ts new file mode 100644 index 000000000..394c632f9 --- /dev/null +++ b/test/models/v2/messages.spec.ts @@ -0,0 +1,45 @@ +import { Messages } from '../../../src/models/v2/messages'; +import { Message } from '../../../src/models/v2/message'; + +const message = { + messageId: 'test', +}; +const messageItem = new Message('test', message); + +describe('Messages model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new Messages([]); + expect(servers.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function () { + const servers = new Messages([messageItem]); + expect(servers.isEmpty()).toEqual(false); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Message if it is present', function () { + const servers = new Messages([messageItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Message is missing', function () { + const servers = new Messages([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said id is available', function () { + const servers = new Messages([messageItem]); + expect(servers.has('test')).toEqual(true); + }) + + it('should return false if the Message id is missing', function () { + const servers = new Messages([messageItem]); + expect(servers.has('anotherId')).toEqual(false); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/oauth-flow.spec.ts b/test/models/v2/oauth-flow.spec.ts new file mode 100644 index 000000000..8435af1cb --- /dev/null +++ b/test/models/v2/oauth-flow.spec.ts @@ -0,0 +1,31 @@ +import { OAuthFlow } from '../../../src/models/v2/oauth-flow'; + +const flowObject = { + "authorizationUrl": "https://example.com/api/oauth/dialog", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } +} + +const flow = new OAuthFlow(flowObject); +const emptyObject = new OAuthFlow({}); + +describe('OAuth Flow', function(){ + describe('.authorizationUrl()', function(){ + it('should reutrn undefined if no authorizationUrl present', function(){ + expect(emptyObject.authorizationUrl()).toBeUndefined(); + }) + + it('should return authrozationUrl ', function(){ + expect(flow.authorizationUrl()).toMatch(flowObject.authorizationUrl); + }) + }) + + describe('.scopes()', function() { + it('should return scopes if present', function() { + expect(emptyObject.scopes()).toBeUndefined(); + expect(flow.scopes()!['write:pets']).toMatch(flowObject.scopes['write:pets']); + }) + }) +}) diff --git a/test/models/v2/oauth-flow.ts b/test/models/v2/oauth-flow.ts deleted file mode 100644 index 3bddb0e84..000000000 --- a/test/models/v2/oauth-flow.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { OAuthFlow } from '../../../src/models/v2/oauth-flow'; - -const flowObject = { - "authorizationUrl": "https://example.com/api/oauth/dialog", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } -} - -const flow = new OAuthFlow(flowObject); -const emptyObject = new OAuthFlow({}); - - -describe('OAuth Flow', function(){ - describe('.authorizationUrl()', function(){ - it('should reutrn undefined if no authorizationUrl present', function(){ - expect(emptyObject.authorizationUrl()).toBeUndefined(); - }) - - it('should return authrozationUrl ', function(){ - expect(flow.authorizationUrl()).toMatch(flowObject.authorizationUrl); - }) - }) - - describe('.scopes()', function() { - it('should return scopes if present', function() { - expect(emptyObject.scopes()).toBeUndefined(); - expect(flow.scopes()['write:pets']).toMatch(flowObject.scopes['write:pets']); - }) - }) - - -}) \ No newline at end of file diff --git a/test/models/v2/oauth-flows.spec.ts b/test/models/v2/oauth-flows.spec.ts new file mode 100644 index 000000000..79f7d6741 --- /dev/null +++ b/test/models/v2/oauth-flows.spec.ts @@ -0,0 +1,36 @@ +import { OAuthFlows } from '../../../src/models/v2/oauth-flows'; +import { OAuthFlow } from '../../../src/models/v2/oauth-flow'; + +const oAuthFlowsObject = { + "implicit": { + "authorizationUrl": "https://example.com/api/oauth/dialog", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "authorizationCode": { + "authorizationUrl": "https://example.com/api/oauth/dialog", + "tokenUrl": "https://example.com/api/oauth/token", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } +} + +const flows = new OAuthFlows(oAuthFlowsObject); + +describe('OAuth Flows', function() { + describe('.hasImplicit()', function(){ + it('should return true', function(){ + expect(flows.hasImplicit()).toBeTruthy(); + }) + }) + + describe('.implicit()', function() { + it('should return OAuthflow object', function() { + expect(flows.implicit() instanceof OAuthFlow).toBeTruthy(); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/oauth-flows.ts b/test/models/v2/oauth-flows.ts deleted file mode 100644 index 3f85ab013..000000000 --- a/test/models/v2/oauth-flows.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {OAuthFlows} from '../../../src/models/v2/oauth-flows'; -import {OAuthFlow} from '../../../src/models/v2/oauth-flow'; - -const oAuthFlowsObject = { - "implicit": { - "authorizationUrl": "https://example.com/api/oauth/dialog", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - }, - "authorizationCode": { - "authorizationUrl": "https://example.com/api/oauth/dialog", - "tokenUrl": "https://example.com/api/oauth/token", - "scopes": { - "write:pets": "modify pets in your account", - "read:pets": "read your pets" - } - } -} - -const flows = new OAuthFlows(oAuthFlowsObject); - -describe('OAuth Flows', function() { - describe('.hasImplicit()', function(){ - it('should return true', function(){ - expect(flows.hasImplicit()).toBeTruthy(); - }) - }) - - describe('.implicit()', function() { - it('should return OAuthflow object', function() { - expect(flows.implicit() instanceof OAuthFlow).toBeTruthy(); - }) - }) -}) \ No newline at end of file diff --git a/test/models/v2/operation-trait.spec.ts b/test/models/v2/operation-trait.spec.ts new file mode 100644 index 000000000..9704ae019 --- /dev/null +++ b/test/models/v2/operation-trait.spec.ts @@ -0,0 +1,117 @@ +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'; + +describe('OperationTrait model', function() { + describe('.id()', function() { + it('should return id of model', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(d.id()).toEqual('trait'); + }); + + it('should reuse operationId', function() { + const doc = { operationId: '...' }; + const d = new OperationTrait('trait', doc); + expect(d.id()).toEqual(doc.operationId); + }); + }); + + describe('.action()', function() { + it('should return kind/action of operation', function() { + const doc = {}; + const d = new OperationTrait('trait', doc, { asyncapi: {} as any, pointer: '', action: 'publish' }); + expect(d.action()).toEqual('publish'); + }); + }); + + describe('.hasOperationId()', function() { + it('should return true when there is a value', function() { + const doc = { operationId: '...' }; + const d = new OperationTrait('trait', doc); + expect(d.hasOperationId()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(d.hasOperationId()).toEqual(false); + }); + }); + + describe('.operationId()', function() { + it('should return the value', function() { + const doc = { operationId: '...' }; + const d = new OperationTrait('trait', doc); + expect(d.operationId()).toEqual(doc.operationId); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(d.operationId()).toBeUndefined(); + }); + }); + + describe('.hasSummary()', function() { + it('should return true when there is a value', function() { + const doc = { summary: "..." }; + const d = new OperationTrait('trait', doc); + expect(d.hasSummary()).toEqual(true); + }); + + it('should return false when there is no value', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(d.hasSummary()).toEqual(false); + }); + }); + + describe('.summary()', function() { + it('should return the value', function() { + const doc = { summary: "..." }; + const d = new OperationTrait('trait', doc); + expect(d.summary()).toEqual(doc.summary); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(d.summary()).toBeUndefined(); + }); + }); + + describe('.security()', function() { + it('should return collection of security requirements', function() { + const doc = { security: [ { requirement: [] } ] }; + const d = new OperationTrait('trait', doc); + expect(Array.isArray(d.security())).toEqual(true); + expect(d.security()).toHaveLength(1); + expect(typeof d.security()[0]).toEqual('object'); + expect(d.security()[0]['requirement'].schema).toBeInstanceOf(SecurityScheme); + expect(d.security()[0]['requirement'].scopes).toEqual([]); + }); + + it('should return collection of security requirements when value is undefined', function() { + const doc = {}; + const d = new OperationTrait('trait', doc); + expect(Array.isArray(d.security())).toEqual(true); + expect(d.security()).toHaveLength(0); + }); + }); + + describe('mixins inheritance', function() { + assertBindingsMixinInheritance(OperationTrait); + assertDescriptionMixinInheritance(OperationTrait); + assertExtensionsMixinInheritance(OperationTrait); + assertExternalDocumentationMixinInheritance(OperationTrait); + assertTagsMixinInheritance(OperationTrait); + }); +}); diff --git a/test/models/v2/operation-traits.spec.ts b/test/models/v2/operation-traits.spec.ts new file mode 100644 index 000000000..6f8bb0c2a --- /dev/null +++ b/test/models/v2/operation-traits.spec.ts @@ -0,0 +1,45 @@ +import { OperationTraits } from '../../../src/models/v2/operation-traits'; +import { OperationTrait } from '../../../src/models/v2/operation-trait'; + +const operationTrait = { + operationId: 'test', +}; +const operationTraitItem = new OperationTrait('test', operationTrait); + +describe('OperationTraits model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new OperationTraits([]); + expect(servers.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function () { + const servers = new OperationTraits([operationTraitItem]); + expect(servers.isEmpty()).toEqual(false); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Operation Trait if it is present', function () { + const servers = new OperationTraits([operationTraitItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Operation Trait is missing', function () { + const servers = new OperationTraits([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said id is available', function () { + const servers = new OperationTraits([operationTraitItem]); + expect(servers.has('test')).toEqual(true); + }) + + it('should return false if the Operation Trait id is missing', function () { + const servers = new OperationTraits([operationTraitItem]); + expect(servers.has('anotherId')).toEqual(false); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/operation.spec.ts b/test/models/v2/operation.spec.ts new file mode 100644 index 000000000..8ff8d0a77 --- /dev/null +++ b/test/models/v2/operation.spec.ts @@ -0,0 +1,80 @@ +import { Operation } from '../../../src/models/v2/operation'; +import { OperationTraits } from '../../../src/models/v2/operation-traits'; +import { OperationTrait } from '../../../src/models/v2/operation-trait'; +import { Messages } from '../../../src/models/v2/messages'; +import { Message } from '../../../src/models/v2/message'; + +import { + assertBindingsMixinInheritance, + assertDescriptionMixinInheritance, + assertExtensionsMixinInheritance, + assertExternalDocumentationMixinInheritance, + assertTagsMixinInheritance, +} from './mixins/inheritance'; + +describe('Operation model', function() { + describe('.id()', function() { + it('should return id of model', function() { + const doc = {}; + const d = new Operation('message', doc); + expect(d.id()).toEqual('message'); + }); + + it('should reuse operationId', function() { + const doc = { operationId: '...' }; + const d = new Operation('message', doc); + expect(d.id()).toEqual(doc.operationId); + }); + }); + + describe('.messages()', function() { + it('should return collection of messages - single message', function() { + const doc = { message: { messageId: '...' } }; + const d = new Operation('message', doc); + expect(d.messages()).toBeInstanceOf(Messages); + expect(d.messages().all()).toHaveLength(1); + expect(d.messages().all()[0]).toBeInstanceOf(Message); + }); + + it('should return collection of messages - oneOf messages', function() { + const doc = { message: { oneOf: [ { messageId: '...' }, { messageId: '...' } ] } }; + const d = new Operation('message', doc); + expect(d.messages()).toBeInstanceOf(Messages); + expect(d.messages().all()).toHaveLength(2); + expect(d.messages().all()[0]).toBeInstanceOf(Message); + expect(d.messages().all()[1]).toBeInstanceOf(Message); + }); + + it('should return undefined when there is no value', function() { + const doc = {}; + const d = new Operation('message', doc); + expect(d.messages()).toBeInstanceOf(Messages); + expect(d.messages().all()).toHaveLength(0); + }); + }); + + describe('.traits()', function() { + it('should return collection of traits', function() { + const doc = { traits: [ { operationId: '...' } ] }; + const d = new Operation('message', doc); + expect(d.traits()).toBeInstanceOf(OperationTraits); + expect(d.traits().all()).toHaveLength(1); + expect(d.traits().all()[0]).toBeInstanceOf(OperationTrait); + }); + + it('should return collection of traits when value is undefined', function() { + const doc = {}; + const d = new Operation('message', doc); + expect(d.traits()).toBeInstanceOf(OperationTraits); + expect(d.traits().all()).toHaveLength(0); + }); + }); + + describe('mixins inheritance', function() { + assertBindingsMixinInheritance(Operation); + assertDescriptionMixinInheritance(Operation); + assertExtensionsMixinInheritance(Operation); + assertExternalDocumentationMixinInheritance(Operation); + assertTagsMixinInheritance(Operation); + }); +}); diff --git a/test/models/v2/operations.spec.ts b/test/models/v2/operations.spec.ts new file mode 100644 index 000000000..ca0a87243 --- /dev/null +++ b/test/models/v2/operations.spec.ts @@ -0,0 +1,45 @@ +import { Operations } from '../../../src/models/v2/operations'; +import { Operation } from '../../../src/models/v2/operation'; + +const operation = { + messageId: 'test', +}; +const operationItem = new Operation('test', operation); + +describe('Operations model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new Operations([]); + expect(servers.isEmpty()).toEqual(true); + }); + + it('should return false if collection is not empty', function () { + const servers = new Operations([operationItem]); + expect(servers.isEmpty()).toEqual(false); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Operation if it is present', function () { + const servers = new Operations([operationItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Operation is missing', function () { + const servers = new Operations([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said id is available', function () { + const servers = new Operations([operationItem]); + expect(servers.has('test')).toEqual(true); + }) + + it('should return false if the Operation id is missing', function () { + const servers = new Operations([operationItem]); + expect(servers.has('anotherId')).toEqual(false); + }) + }) +}) \ No newline at end of file diff --git a/test/models/v2/server-variable.spec.ts b/test/models/v2/server-variable.spec.ts new file mode 100644 index 000000000..42d420d43 --- /dev/null +++ b/test/models/v2/server-variable.spec.ts @@ -0,0 +1,39 @@ +import { ServerVariable } from '../../../src/models/v2/server-variable'; + +const doc = { + description: 'Secure connection (TLS) is available through port 8883.', + default: '1883', + enum: ['1883', '8883'] +} + +const sv = new ServerVariable('doc', doc); + +describe('Server Variable ', function() { + describe('.id()', function() { + expect(sv.id()).toMatch('doc'); + }) + + describe('.hasDefaultValue()', function() { + it('should return true if default value is passed', function(){ + expect(sv.hasDefaultValue()).toBeTruthy(); + }) + }) + + describe('.defaultValue()', function(){ + it('should return default value', function() { + expect(sv.defaultValue()).toMatch(doc.default); + }) + }) + + describe('.hasAllowedValue()', function() { + it('should return true when enum is passed', function(){ + expect(sv.hasAllowedValues()).toBeTruthy(); + }) + }) + + describe('.allowedValue()', function(){ + it('should return enum object', function(){ + expect(sv.allowedValues()).toEqual(doc.enum) + }) + }) +}) diff --git a/test/models/v2/server-variable.ts b/test/models/v2/server-variable.ts deleted file mode 100644 index 4ff3fef4d..000000000 --- a/test/models/v2/server-variable.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {ServerVariable} from '../../../src/models/v2/server-variable'; - -const doc = { - description: 'Secure connection (TLS) is available through port 8883.', - default: '1883', - enum: ['1883', '8883'] -} - -const sv = new ServerVariable('doc', doc); - -describe('server variable ', function() { - describe('.id()', function() { - expect(sv.id()).toMatch('doc'); - }) - - describe('.hasDefaultValue()', function() { - it('should return true if default value is passed', function(){ - expect(sv.hasDefaultValue()).toBeTruthy(); - }) - }) - - describe('.defaultValue()', function(){ - it('should return default value', function() { - expect(sv.defaultValue()).toMatch(doc.default); - }) - }) - - describe('.hasAllowedValue()', function() { - it('should return true when enum is passed', function(){ - expect(sv.hasAllowedValue()).toBeTruthy(); - }) - }) - - describe('.allowedValue()', function(){ - it('should return enum object', function(){ - expect(sv.allowedValue()).toEqual(doc.enum) - }) - }) -}) \ No newline at end of file diff --git a/test/models/v2/server.spec.ts b/test/models/v2/server.spec.ts index 39dcc7f7f..f724b0276 100644 --- a/test/models/v2/server.spec.ts +++ b/test/models/v2/server.spec.ts @@ -1,7 +1,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'; @@ -61,13 +63,33 @@ describe('Server Model', function () { }); }); - describe('.servers()', function () { + describe('.variables()', function () { it('should return ServerVariables object', function () { - expect(docItem.variables() instanceof ServerVariables).toBeTruthy(); + expect(docItem.variables()).toBeInstanceOf(ServerVariables); }) }) + describe('.security()', function() { + it('should return collection of security requirements', function() { + const doc = { security: [ { requirement: [] } ] }; + const d = new Server('trait', doc); + expect(Array.isArray(d.security())).toEqual(true); + expect(d.security()).toHaveLength(1); + expect(typeof d.security()[0]).toEqual('object'); + expect(d.security()[0]['requirement'].schema).toBeInstanceOf(SecurityScheme); + expect(d.security()[0]['requirement'].scopes).toEqual([]); + }); + + it('should return collection of security requirements when value is undefined', function() { + const doc = {}; + const d = new Server('trait', doc); + expect(Array.isArray(d.security())).toEqual(true); + expect(d.security()).toHaveLength(0); + }); + }); + describe('mixins inheritance', function () { + assertBindingsMixinInheritance(Server); assertDescriptionMixinInheritance(Server); assertExtensionsMixinInheritance(Server); }); diff --git a/test/models/v2/servers.spec.ts b/test/models/v2/servers.spec.ts index f76ff7f3c..122ad7d09 100644 --- a/test/models/v2/servers.spec.ts +++ b/test/models/v2/servers.spec.ts @@ -14,17 +14,17 @@ describe('Servers model', function () { describe('.isEmpty()', function () { it('should return true if collection is empty', function () { const servers = new Servers([]); - expect(servers.isEmpty()).toBeTruthy(); + expect(servers.isEmpty()).toEqual(true); }); it('should return false if collection is not empty', function () { const servers = new Servers([docItem]); - expect(servers.isEmpty()).toBeFalsy(); + expect(servers.isEmpty()).toEqual(false); }); }); describe('.get(id)', function () { - it('should return a specific server Object if it is present', function () { + it('should return a specific Server Object if it is present', function () { const servers = new Servers([docItem]); expect(servers.get('development')).toBeTruthy(); }); @@ -36,14 +36,14 @@ describe('Servers model', function () { }); describe('.has(id)', function () { - const servers = new Servers([docItem]); - it('should return true if the said name is available', function () { - expect(servers.has('development')).toBeTruthy(); + const servers = new Servers([docItem]); + expect(servers.has('development')).toEqual(true); }) it('should return false if the server name is missing', function () { - expect(servers.has('production')).toBeFalsy(); + const servers = new Servers([docItem]); + expect(servers.has('production')).toEqual(false); }) }) }) \ No newline at end of file diff --git a/test/models/v2/tags.spec.ts b/test/models/v2/tags.spec.ts new file mode 100644 index 000000000..b6b65807e --- /dev/null +++ b/test/models/v2/tags.spec.ts @@ -0,0 +1,44 @@ +import { Tags, Tag } from '../../../src/models/v2/mixins/tags'; + +const tag = { + name: 'test', +}; +const tagItem = new Tag(tag); + +describe('Tags model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new Tags([]); + expect(servers.isEmpty()).toBeTruthy(); + }); + + it('should return false if collection is not empty', function () { + const servers = new Tags([tagItem]); + expect(servers.isEmpty()).toBeFalsy(); + }); + }); + + describe('.get(id)', function () { + it('should return a specific Tag Object if it is present', function () { + const servers = new Tags([tagItem]); + expect(servers.get('test')).toBeTruthy(); + }); + + it('should return undefined if specific Tag Object is missing', function () { + const servers = new Tags([]); + expect(servers.get('test')).toBeUndefined(); + }); + }); + + describe('.has(id)', function () { + it('should return true if the said name is available', function () { + const servers = new Tags([tagItem]); + expect(servers.has('test')).toBeTruthy(); + }) + + it('should return false if the Tag name is missing', function () { + const servers = new Tags([tagItem]); + expect(servers.has('anotherName')).toBeFalsy(); + }) + }) +}) \ No newline at end of file