diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 000000000..1b41ae5b3 --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1,2 @@ +# Disable specific duplicate code since it would introduce more complexity to reduce it. +sonar.cpd.exclusions=src/models/**/*.ts \ No newline at end of file diff --git a/src/models/asyncapi.ts b/src/models/asyncapi.ts index 6fb68a1ab..0c20dfcb5 100644 --- a/src/models/asyncapi.ts +++ b/src/models/asyncapi.ts @@ -4,10 +4,12 @@ import { AsyncAPIDocumentV3 } from "./v3"; import type { InfoInterface } from "./info"; import type { BaseModel } from "./base"; import type { ExtensionsMixinInterface } from "./mixins"; +import { ServersInterface } from "./servers"; export interface AsyncAPIDocumentInterface extends BaseModel, ExtensionsMixinInterface { version(): string; info(): InfoInterface; + servers(): ServersInterface } export function newAsyncAPIDocument(json: Record): AsyncAPIDocumentInterface { diff --git a/src/models/server.ts b/src/models/server.ts new file mode 100644 index 000000000..d9526fd3d --- /dev/null +++ b/src/models/server.ts @@ -0,0 +1,10 @@ +import { BaseModel } from "./base"; +import { BindingsMixinInterface, DescriptionMixinInterface } from './mixins'; + +export interface ServerInterface extends BaseModel, DescriptionMixinInterface, BindingsMixinInterface { + id(): string + protocol(): string | undefined; + protocolVersion(): string; + hasProtocolVersion(): boolean; + url(): string; +} \ No newline at end of file diff --git a/src/models/servers.ts b/src/models/servers.ts new file mode 100644 index 000000000..d8360d487 --- /dev/null +++ b/src/models/servers.ts @@ -0,0 +1,4 @@ +import { Collection } from "./collection"; +import {ServerInterface} from "./server"; + +export interface ServersInterface extends Collection {} \ No newline at end of file diff --git a/src/models/utils.ts b/src/models/utils.ts index 0b73d948a..e2befd7a8 100644 --- a/src/models/utils.ts +++ b/src/models/utils.ts @@ -32,4 +32,14 @@ function mixin(derivedCtor: any, constructors: any[]): typeof BaseModel { }); }); return derivedCtor; +} + +export function createArrayFromMap(json: Record){ + const ArrayObject = []; + for (const [key, value] of Object.entries(json)) { + value['id'] = key; + ArrayObject.push(value); + }; + + return ArrayObject; } \ No newline at end of file diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index e690f3fcb..a220e9fa9 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -5,9 +5,12 @@ import { Mixin } from '../utils'; import { ExtensionsMixin } from './mixins/extensions'; import { AsyncAPIDocumentInterface, InfoInterface } from "../../models"; +import { ServersInterface } from "models/servers"; +import { Servers } from "./servers"; +import { Server } from "./server"; -export class AsyncAPIDocument - extends Mixin(BaseModel, ExtensionsMixin) +export class AsyncAPIDocument + extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { version(): string { @@ -17,4 +20,10 @@ export class AsyncAPIDocument info(): InfoInterface { return new Info(this._json.info); } + + servers(): ServersInterface { + return new Servers( + Object.entries(this._json.servers).map(([serverName, server]) => new Server(serverName, server as Record)) + ); + } } diff --git a/src/models/v2/index.ts b/src/models/v2/index.ts index 34c57b438..64a301036 100644 --- a/src/models/v2/index.ts +++ b/src/models/v2/index.ts @@ -5,4 +5,6 @@ export { License as LicenseV2 } from './license'; export { Bindings as BindingsV2, Binding as BindingV2 } from './mixins/bindings'; export { Extensions as ExtensionsV2, Extension as ExtensionV2 } from './mixins/extensions'; export { ExternalDocumentation as ExternalDocumentationV2 } from './mixins/external-docs'; -export { Tags as TagsV2, Tag as TagV2 } from './mixins/tags'; \ No newline at end of file +export { Tags as TagsV2, Tag as TagV2 } from './mixins/tags'; +export { Server as ServerV2 } from './server'; +export { Servers as ServersV2 } from './servers'; \ No newline at end of file diff --git a/src/models/v2/server.ts b/src/models/v2/server.ts new file mode 100644 index 000000000..3f52dc724 --- /dev/null +++ b/src/models/v2/server.ts @@ -0,0 +1,34 @@ +import { Mixin } from '../utils'; +import { BaseModel } from '../base'; +import { ServerInterface } from '../server'; +import { DescriptionMixin } from './mixins/description'; +import { BindingsMixin } from './mixins/bindings'; + +export class Server extends Mixin(BaseModel, DescriptionMixin, BindingsMixin) implements ServerInterface { + constructor( + private readonly _id: string, + _json: Record + ){ + super(_json); + } + + id(): string { + return this._id; + } + + protocol(): string | undefined { + return this.json('protocol'); + } + + hasProtocolVersion(): boolean { + return !!this.json('protocolVersion'); + } + + protocolVersion(): string { + return this.json('protocolVersion'); + } + + url(): string { + return this.json('url'); + } +} \ No newline at end of file diff --git a/src/models/v2/servers.ts b/src/models/v2/servers.ts new file mode 100644 index 000000000..c8e9fa0dc --- /dev/null +++ b/src/models/v2/servers.ts @@ -0,0 +1,13 @@ +import { Collection } from '../collection'; +import { ServerInterface } from '../server'; +import { ServersInterface } from '../servers'; + +export class Servers extends Collection implements ServersInterface { + override get(id: string): ServerInterface | undefined { + return this.collections.find(server => server.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(server => server.id() === id); + } +} \ No newline at end of file diff --git a/src/models/v3/asyncapi.ts b/src/models/v3/asyncapi.ts index f48ee2ea6..5da958d7c 100644 --- a/src/models/v3/asyncapi.ts +++ b/src/models/v3/asyncapi.ts @@ -4,11 +4,14 @@ import { Info } from "./info"; import { Mixin } from '../utils'; import { ExtensionsMixin } from './mixins/extensions'; +import { ServersInterface } from "models/servers"; +import { Servers } from "./servers"; +import { Server } from "./server"; export class AsyncAPIDocument extends Mixin(BaseModel, ExtensionsMixin) implements AsyncAPIDocumentInterface { - + version(): string { return this.json("asyncapi"); } @@ -16,4 +19,12 @@ export class AsyncAPIDocument info(): Info { return new Info(this.json("info")); } + + servers(): ServersInterface { + return new Servers( + Object.entries(this._json.servers).map( + ([serverName, server]) => new Server(serverName, server as Record) + ) + ) + } } diff --git a/src/models/v3/index.ts b/src/models/v3/index.ts index 967ba16c8..c974ffbeb 100644 --- a/src/models/v3/index.ts +++ b/src/models/v3/index.ts @@ -5,4 +5,6 @@ export { License as LicenseV3 } from './license'; export { Bindings as BindingsV3, Binding as BindingV3 } from './mixins/bindings'; export { Extensions as ExtensionsV3, Extension as ExtensionV3 } from './mixins/extensions'; export { ExternalDocumentation as ExternalDocumentationV3 } from './mixins/external-docs'; -export { Tags as TagsV3, Tag as TagV3 } from './mixins/tags'; \ No newline at end of file +export { Tags as TagsV3, Tag as TagV3 } from './mixins/tags'; +export { Server as ServerV3 } from './server'; +export { Servers as ServersV3 } from './servers'; \ No newline at end of file diff --git a/src/models/v3/server.ts b/src/models/v3/server.ts new file mode 100644 index 000000000..3f52dc724 --- /dev/null +++ b/src/models/v3/server.ts @@ -0,0 +1,34 @@ +import { Mixin } from '../utils'; +import { BaseModel } from '../base'; +import { ServerInterface } from '../server'; +import { DescriptionMixin } from './mixins/description'; +import { BindingsMixin } from './mixins/bindings'; + +export class Server extends Mixin(BaseModel, DescriptionMixin, BindingsMixin) implements ServerInterface { + constructor( + private readonly _id: string, + _json: Record + ){ + super(_json); + } + + id(): string { + return this._id; + } + + protocol(): string | undefined { + return this.json('protocol'); + } + + hasProtocolVersion(): boolean { + return !!this.json('protocolVersion'); + } + + protocolVersion(): string { + return this.json('protocolVersion'); + } + + url(): string { + return this.json('url'); + } +} \ No newline at end of file diff --git a/src/models/v3/servers.ts b/src/models/v3/servers.ts new file mode 100644 index 000000000..c8e9fa0dc --- /dev/null +++ b/src/models/v3/servers.ts @@ -0,0 +1,13 @@ +import { Collection } from '../collection'; +import { ServerInterface } from '../server'; +import { ServersInterface } from '../servers'; + +export class Servers extends Collection implements ServersInterface { + override get(id: string): ServerInterface | undefined { + return this.collections.find(server => server.id() === id); + } + + override has(id: string): boolean { + return this.collections.some(server => server.id() === id); + } +} \ No newline at end of file diff --git a/test/models/v2/asyncapi.spec.ts b/test/models/v2/asyncapi.spec.ts index 24c98cbc9..60ee2499c 100644 --- a/test/models/v2/asyncapi.spec.ts +++ b/test/models/v2/asyncapi.spec.ts @@ -1,4 +1,4 @@ -import { newAsyncAPIDocument, AsyncAPIDocumentV2, InfoV2, AsyncAPIDocumentV3 } from '../../../src/models'; +import { newAsyncAPIDocument, AsyncAPIDocumentV2, InfoV2, AsyncAPIDocumentV3, ServersV2 } from '../../../src/models'; import { assertExtensionsMixinInheritance, @@ -27,6 +27,18 @@ describe('AsyncAPIDocument model', function() { }); }); + describe('.servers()', function(){ + it('should return an servers object', function(){ + const doc = {servers: { + development: { + + } + }}; + const d = new AsyncAPIDocumentV2(doc); + expect(d.servers() instanceof ServersV2).toBeTruthy(); + }) + }) + describe('mixins inheritance', function() { assertExtensionsMixinInheritance(AsyncAPIDocumentV2); }); diff --git a/test/models/v2/server.spec.ts b/test/models/v2/server.spec.ts new file mode 100644 index 000000000..ccb318834 --- /dev/null +++ b/test/models/v2/server.spec.ts @@ -0,0 +1,92 @@ +import { Server } from '../../../src/models/v2/server'; +import { Servers } from '../../../src/models/v2/servers'; + +const doc = { + 'development': { + protocol: 'mqtt', + protocolVersion: '1.0.0', + url: 'development.gigantic-server.com' + } +}; +const docItem = new Server('development', doc.development); +const emptyItem = new Server('',{}); + +describe('Servers model', function () { + describe('.isEmpty()', function () { + it('should return true if collection is empty', function () { + const servers = new Servers([]); + expect(servers.isEmpty()).toBeTruthy(); + }); + + it('should return false if collection is not empty', function () { + const servers = new Servers([docItem]); + expect(servers.isEmpty()).toBeFalsy(); + }); + }) + + describe('.get(id)', function () { + it('should return a specific server Object if it is present', function () { + const servers = new Servers([docItem]); + expect(servers.get('development')).toBeTruthy(); + }); + + it('should return undefined if a server is said Id is missing ', function () { + const servers = new Servers([]); + expect(servers.get('development')).toBeUndefined(); + }); + }) + + 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(); + }) + + it('should return false if the server name is missing', function () { + expect(servers.has('production')).toBeFalsy(); + }) + }) +}) + +describe('Server Model', function () { + + describe('.id()', function () { + it('should return name if present', function () { + expect(docItem.id()).toMatch('development'); + }); + }); + + describe('protocol()', function () { + it('should return protocol ', function () { + expect(docItem.protocol()).toMatch(doc.development.protocol); + }); + }); + + describe('.hasProtocolVersion()', function () { + it('should return true if protocolVersion is not missing', function () { + expect(docItem.hasProtocolVersion()).toBeTruthy(); + }); + + it('should be false when protocolVersion is missing', function () { + expect(emptyItem.hasProtocolVersion()).toBeFalsy(); + }); + }) + + describe('.protocolVersion()', function () { + it('should return protocolVersion', function () { + expect(docItem.protocolVersion()).toMatch(doc.development.protocolVersion); + }); + + it('should return undefined protocolVersion when protocolVersion is missing', function () { + expect(emptyItem.protocolVersion()).toBeUndefined(); + }) + }) + + describe('.url()', function () { + it('should return url', function () { + expect(docItem.url()).toMatch(doc.development.url); + }); + }); +}) \ No newline at end of file