diff --git a/src/models/message-trait.ts b/src/models/message-trait.ts index b140fdfe6..8abb1f18e 100644 --- a/src/models/message-trait.ts +++ b/src/models/message-trait.ts @@ -6,7 +6,8 @@ import type { SchemaInterface } from './schema'; export interface MessageTraitInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface { id(): string; - schemaFormat(): string; + hasSchemaFormat(): boolean; + schemaFormat(): string | undefined; hasMessageId(): boolean; messageId(): string | undefined; hasCorrelationId(): boolean; diff --git a/src/models/schema.ts b/src/models/schema.ts index dbcebe489..bc1f5d42c 100644 --- a/src/models/schema.ts +++ b/src/models/schema.ts @@ -49,6 +49,7 @@ export interface SchemaInterface extends BaseModel, Ext propertyNames(): SchemaInterface | undefined; readOnly(): boolean | undefined; required(): Array | undefined; + schemaFormat(): string then(): SchemaInterface | undefined; title(): string | undefined; type(): string | Array | undefined; diff --git a/src/models/v2/message-trait.ts b/src/models/v2/message-trait.ts index 299b7856c..cee11664a 100644 --- a/src/models/v2/message-trait.ts +++ b/src/models/v2/message-trait.ts @@ -5,8 +5,8 @@ import { MessageExample } from './message-example'; import { Schema } from './schema'; import { xParserMessageName } from '../../constants'; -import { getDefaultSchemaFormat } from '../../schema-parser'; import { bindings, hasDescription, description, extensions, hasExternalDocs, externalDocs, tags } from './mixins'; +import { getDefaultSchemaFormat } from '../../schema-parser'; import type { BindingsInterface } from '../bindings'; import type { CorrelationIdInterface } from '../correlation-id'; @@ -24,7 +24,11 @@ export class MessageTrait implements MessageIn payload(): SchemaInterface | undefined { if (!this._json.payload) return undefined; - return this.createModel(Schema, this._json.payload, { pointer: `${this._meta.pointer}/payload` }); + return this.createModel(Schema, this._json.payload, { pointer: `${this._meta.pointer}/payload`, schemaFormat: this._json.schemaFormat }); } servers(): ServersInterface { diff --git a/src/models/v2/schema.ts b/src/models/v2/schema.ts index 20ae87529..9f2df8600 100644 --- a/src/models/v2/schema.ts +++ b/src/models/v2/schema.ts @@ -2,6 +2,7 @@ import { BaseModel } from '../base'; import { xParserSchemaId } from '../../constants'; import { extensions, hasExternalDocs, externalDocs } from './mixins'; +import { getDefaultSchemaFormat } from '../../schema-parser'; import type { ExtensionsInterface } from '../extensions'; import type { ExternalDocumentationInterface } from '../external-docs'; @@ -9,7 +10,7 @@ import type { SchemaInterface } from '../schema'; import type { v2 } from '../../spec-types'; -export class Schema extends BaseModel implements SchemaInterface { +export class Schema extends BaseModel implements SchemaInterface { id(): string { return this.$id() || this._meta.id || this.json(xParserSchemaId as any) as string; } @@ -267,6 +268,10 @@ export class Schema extends BaseModel() as string; } - schemaFormat(): string { - return this._json.schemaFormat || getDefaultSchemaFormat(this._meta.asyncapi.semver.version); - } - hasMessageId(): boolean { return !!this._json.messageId; } + hasSchemaFormat(): boolean { + return false; + } + + schemaFormat(): string | undefined { + return undefined; + } + messageId(): string | undefined { return this._json.messageId; } diff --git a/src/models/v3/message.ts b/src/models/v3/message.ts index 07fc5bf25..ce05a9e3d 100644 --- a/src/models/v3/message.ts +++ b/src/models/v3/message.ts @@ -25,7 +25,20 @@ export class Message extends MessageTrait implements MessageIn payload(): SchemaInterface | undefined { if (!this._json.payload) return undefined; - return this.createModel(Schema, this._json.payload, { pointer: this.jsonPath('payload') }); + return this.createModel(Schema, this._json.payload, { pointer: this.jsonPath('payload')}); + } + + hasSchemaFormat(): boolean { + // If it has a payload, schema format is expected (at least the default) + return this.hasPayload(); + } + + schemaFormat(): string | undefined { + if (this.hasSchemaFormat()) { + return this.payload()?.schemaFormat(); + } + + return undefined; } servers(): ServersInterface { diff --git a/src/models/v3/schema.ts b/src/models/v3/schema.ts index 889f254d7..763c91a45 100644 --- a/src/models/v3/schema.ts +++ b/src/models/v3/schema.ts @@ -1,304 +1,318 @@ -import { BaseModel } from '../base'; +import { BaseModel, ModelMetadata } from '../base'; import { xParserSchemaId } from '../../constants'; import { extensions, hasExternalDocs, externalDocs } from './mixins'; -import { retrievePossibleRef, hasRef } from '../../utils'; +import { getDefaultSchemaFormat } from '../../schema-parser'; +import { AsyncAPISchemaObject } from 'spec-types/v3'; -import type { ModelMetadata } from '../base'; import type { ExtensionsInterface } from '../extensions'; import type { ExternalDocumentationInterface } from '../external-docs'; import type { SchemaInterface } from '../schema'; import type { v3 } from '../../spec-types'; -export class Schema extends BaseModel implements SchemaInterface { +export class Schema extends BaseModel implements SchemaInterface { + private _schemaFormat: string; + private _schemaObject: AsyncAPISchemaObject; + + // The following constructor is needed because, starting from AsyncAPI v3, schemas can be multi-format as well. constructor( - _json: v3.AsyncAPISchemaObject, - _meta: ModelMetadata & { id?: string, parent?: Schema } = {} as any, + protected readonly _json: v3.MultiFormatSchemaObject, + protected readonly _meta: ModelMetadata & { id?: string, parent?: Schema } = {} as ModelMetadata & { id?: string, parent?: Schema }, ) { - _json = retrievePossibleRef(_json, _meta.pointer as string, _meta.asyncapi?.parsed); super(_json, _meta); + + // Based on the shape of the JSON, we grab the data for the Schema from the root (Schema) or rather from `schema` field (MultiFormatSchema). + if (typeof _json === 'object' && typeof _json.schema === 'object') { + this._schemaObject = _json.schema; + this._schemaFormat = _json.schemaFormat; + } else { + this._schemaObject = _json; + this._schemaFormat = getDefaultSchemaFormat(_meta.asyncapi?.semver?.version); + } } - id(): string { - return this.$id() || this._meta.id || this.json(xParserSchemaId as any) as string; + return this.$id() || this._meta.id || (this._schemaObject as any)[xParserSchemaId] as string; } $comment(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.$comment; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.$comment; } $id(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.$id; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.$id; } $schema(): string { - if (typeof this._json === 'boolean') return 'http://json-schema.org/draft-07/schema#'; - return this._json.$schema || 'http://json-schema.org/draft-07/schema#'; + if (typeof this._schemaObject === 'boolean') return 'http://json-schema.org/draft-07/schema#'; + return this._schemaObject.$schema || 'http://json-schema.org/draft-07/schema#'; } additionalItems(): boolean | SchemaInterface { - if (typeof this._json === 'boolean') return this._json; - if (this._json.additionalItems === undefined) return true; - if (typeof this._json.additionalItems === 'boolean') return this._json.additionalItems; - return this.createModel(Schema, this._json.additionalItems, { pointer: `${this._meta.pointer}/additionalItems`, parent: this }); + if (typeof this._schemaObject === 'boolean') return this._schemaObject; + if (this._schemaObject.additionalItems === undefined) return true; + if (typeof this._schemaObject.additionalItems === 'boolean') return this._schemaObject.additionalItems; + return this.createModel(Schema, this._schemaObject.additionalItems, { pointer: `${this._meta.pointer}/additionalItems`, parent: this }); } additionalProperties(): boolean | SchemaInterface { - if (typeof this._json === 'boolean') return this._json; - if (this._json.additionalProperties === undefined) return true; - if (typeof this._json.additionalProperties === 'boolean') return this._json.additionalProperties; - return this.createModel(Schema, this._json.additionalProperties, { pointer: `${this._meta.pointer}/additionalProperties`, parent: this }); + if (typeof this._schemaObject === 'boolean') return this._schemaObject; + if (this._schemaObject.additionalProperties === undefined) return true; + if (typeof this._schemaObject.additionalProperties === 'boolean') return this._schemaObject.additionalProperties; + return this.createModel(Schema, this._schemaObject.additionalProperties, { pointer: `${this._meta.pointer}/additionalProperties`, parent: this }); } allOf(): Array | undefined { - if (typeof this._json === 'boolean') return; - if (!Array.isArray(this._json.allOf)) return undefined; - return this._json.allOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/allOf/${index}`, parent: this })); + if (typeof this._schemaObject === 'boolean') return; + if (!Array.isArray(this._schemaObject.allOf)) return undefined; + return this._schemaObject.allOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/allOf/${index}`, parent: this })); } anyOf(): Array | undefined { - if (typeof this._json === 'boolean') return; - if (!Array.isArray(this._json.anyOf)) return undefined; - return this._json.anyOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/anyOf/${index}`, parent: this })); + if (typeof this._schemaObject === 'boolean') return; + if (!Array.isArray(this._schemaObject.anyOf)) return undefined; + return this._schemaObject.anyOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/anyOf/${index}`, parent: this })); } const(): any { - if (typeof this._json === 'boolean') return; - return this._json.const; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.const; } contains(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.contains !== 'object') return; - return this.createModel(Schema, this._json.contains, { pointer: `${this._meta.pointer}/contains`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.contains !== 'object') return; + return this.createModel(Schema, this._schemaObject.contains, { pointer: `${this._meta.pointer}/contains`, parent: this }); } contentEncoding(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.contentEncoding; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.contentEncoding; } contentMediaType(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.contentMediaType; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.contentMediaType; } default(): any { - if (typeof this._json === 'boolean') return; - return this._json.default; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.default; } definitions(): Record | undefined { - if (typeof this._json === 'boolean' || typeof this._json.definitions !== 'object') return; - return Object.entries(this._json.definitions).reduce((acc: Record, [key, s]: [string, any]) => { + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.definitions !== 'object') return; + return Object.entries(this._schemaObject.definitions).reduce((acc: Record, [key, s]: [string, any]) => { acc[key] = this.createModel(Schema, s, { pointer: `${this._meta.pointer}/definitions/${key}`, parent: this }); return acc; }, {}); } description(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.description; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.description; } dependencies(): Record> | undefined { - if (typeof this._json === 'boolean') return; - if (typeof this._json.dependencies !== 'object') return undefined; - return Object.entries(this._json.dependencies).reduce((acc: Record>, [key, s]: [string, any]) => { + if (typeof this._schemaObject === 'boolean') return; + if (typeof this._schemaObject.dependencies !== 'object') return undefined; + return Object.entries(this._schemaObject.dependencies).reduce((acc: Record>, [key, s]: [string, any]) => { acc[key] = Array.isArray(s) ? s : this.createModel(Schema, s, { pointer: `${this._meta.pointer}/dependencies/${key}`, parent: this }); return acc; }, {}); } deprecated(): boolean { - if (typeof this._json === 'boolean') return false; - return this._json.deprecated || false; + if (typeof this._schemaObject === 'boolean') return false; + return this._schemaObject.deprecated || false; } discriminator(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.discriminator; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.discriminator; } else(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.else !== 'object') return; - return this.createModel(Schema, this._json.else, { pointer: `${this._meta.pointer}/else`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.else !== 'object') return; + return this.createModel(Schema, this._schemaObject.else, { pointer: `${this._meta.pointer}/else`, parent: this }); } enum(): Array | undefined { - if (typeof this._json === 'boolean') return; - return this._json.enum; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.enum; } examples(): Array | undefined { - if (typeof this._json === 'boolean') return; - return this._json.examples; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.examples; } exclusiveMaximum(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.exclusiveMaximum; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.exclusiveMaximum; } exclusiveMinimum(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.exclusiveMinimum; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.exclusiveMinimum; } format(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.format; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.format; } isBooleanSchema(): boolean { - return typeof this._json === 'boolean'; + return typeof this._schemaObject === 'boolean'; } if(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.if !== 'object') return; - return this.createModel(Schema, this._json.if, { pointer: `${this._meta.pointer}/if`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.if !== 'object') return; + return this.createModel(Schema, this._schemaObject.if, { pointer: `${this._meta.pointer}/if`, parent: this }); } isCircular(): boolean { - if (hasRef(this._json)) return true; let parent = this._meta.parent; while (parent) { - if (parent._json === this._json) return true; + if (parent._json === this._schemaObject) return true; parent = parent._meta.parent; } return false; } items(): SchemaInterface | Array | undefined { - if (typeof this._json === 'boolean' || typeof this._json.items !== 'object') return; - if (Array.isArray(this._json.items)) { - return this._json.items.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/items/${index}`, parent: this })); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.items !== 'object') return; + if (Array.isArray(this._schemaObject.items)) { + return this._schemaObject.items.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/items/${index}`, parent: this })); } - return this.createModel(Schema, this._json.items, { pointer: `${this._meta.pointer}/items`, parent: this }); + return this.createModel(Schema, this._schemaObject.items, { pointer: `${this._meta.pointer}/items`, parent: this }); } maximum(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.maximum; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.maximum; } maxItems(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.maxItems; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.maxItems; } maxLength(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.maxLength; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.maxLength; } maxProperties(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.maxProperties; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.maxProperties; } minimum(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.minimum; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.minimum; } minItems(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.minItems; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.minItems; } minLength(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.minLength; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.minLength; } minProperties(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.minProperties; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.minProperties; } multipleOf(): number | undefined { - if (typeof this._json === 'boolean') return; - return this._json.multipleOf; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.multipleOf; } not(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.not !== 'object') return; - return this.createModel(Schema, this._json.not, { pointer: `${this._meta.pointer}/not`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.not !== 'object') return; + return this.createModel(Schema, this._schemaObject.not, { pointer: `${this._meta.pointer}/not`, parent: this }); } oneOf(): Array | undefined { - if (typeof this._json === 'boolean') return; - if (!Array.isArray(this._json.oneOf)) return undefined; - return this._json.oneOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/oneOf/${index}`, parent: this })); + if (typeof this._schemaObject === 'boolean') return; + if (!Array.isArray(this._schemaObject.oneOf)) return undefined; + return this._schemaObject.oneOf.map((s, index) => this.createModel(Schema, s, { pointer: `${this._meta.pointer}/oneOf/${index}`, parent: this })); } pattern(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.pattern; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.pattern; } patternProperties(): Record | undefined { - if (typeof this._json === 'boolean' || typeof this._json.patternProperties !== 'object') return; - return Object.entries(this._json.patternProperties).reduce((acc: Record, [key, s]: [string, any]) => { + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.patternProperties !== 'object') return; + return Object.entries(this._schemaObject.patternProperties).reduce((acc: Record, [key, s]: [string, any]) => { acc[key] = this.createModel(Schema, s, { pointer: `${this._meta.pointer}/patternProperties/${key}`, parent: this }); return acc; }, {}); } properties(): Record | undefined { - if (typeof this._json === 'boolean' || typeof this._json.properties !== 'object') return; - return Object.entries(this._json.properties).reduce((acc: Record, [key, s]: [string, any]) => { + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.properties !== 'object') return; + return Object.entries(this._schemaObject.properties).reduce((acc: Record, [key, s]: [string, any]) => { acc[key] = this.createModel(Schema, s, { pointer: `${this._meta.pointer}/properties/${key}`, parent: this }); return acc; }, {}); } property(name: string): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.properties !== 'object' || typeof this._json.properties[name] !== 'object') return; - return this.createModel(Schema, this._json.properties[name], { pointer: `${this._meta.pointer}/properties/${name}`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.properties !== 'object' || typeof this._schemaObject.properties[name] !== 'object') return; + return this.createModel(Schema, this._schemaObject.properties[name], { pointer: `${this._meta.pointer}/properties/${name}`, parent: this }); } propertyNames(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.propertyNames !== 'object') return; - return this.createModel(Schema, this._json.propertyNames, { pointer: `${this._meta.pointer}/propertyNames`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.propertyNames !== 'object') return; + return this.createModel(Schema, this._schemaObject.propertyNames, { pointer: `${this._meta.pointer}/propertyNames`, parent: this }); } readOnly(): boolean | undefined { - if (typeof this._json === 'boolean') return false; - return this._json.readOnly || false; + if (typeof this._schemaObject === 'boolean') return false; + return this._schemaObject.readOnly || false; } required(): Array | undefined { - if (typeof this._json === 'boolean') return; - return this._json.required; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.required; + } + + schemaFormat(): string { + return this._schemaFormat; } then(): SchemaInterface | undefined { - if (typeof this._json === 'boolean' || typeof this._json.then !== 'object') return; - return this.createModel(Schema, this._json.then, { pointer: `${this._meta.pointer}/then`, parent: this }); + if (typeof this._schemaObject === 'boolean' || typeof this._schemaObject.then !== 'object') return; + return this.createModel(Schema, this._schemaObject.then, { pointer: `${this._meta.pointer}/then`, parent: this }); } title(): string | undefined { - if (typeof this._json === 'boolean') return; - return this._json.title; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.title; } type(): string | Array | undefined { - if (typeof this._json === 'boolean') return; - return this._json.type; + if (typeof this._schemaObject === 'boolean') return; + return this._schemaObject.type; } uniqueItems(): boolean | undefined { - if (typeof this._json === 'boolean') return false; - return this._json.uniqueItems || false; + if (typeof this._schemaObject === 'boolean') return false; + return this._schemaObject.uniqueItems || false; } writeOnly(): boolean | undefined { - if (typeof this._json === 'boolean') return false; - return this._json.writeOnly || false; + if (typeof this._schemaObject === 'boolean') return false; + return this._schemaObject.writeOnly || false; } hasExternalDocs(): boolean { diff --git a/src/spec-types/v3.ts b/src/spec-types/v3.ts index 08a337731..e0fc84991 100644 --- a/src/spec-types/v3.ts +++ b/src/spec-types/v3.ts @@ -402,8 +402,9 @@ export interface OAuthFlowObjectAuthorizationCode extends OAuthFlowObjectBase, S } export type SchemaObject = AsyncAPISchemaObject | ReferenceObject; - export type AsyncAPISchemaObject = AsyncAPISchemaDefinition | boolean; +export type MultiFormatSchemaObject = AsyncAPISchemaObject | { schema: AsyncAPISchemaObject, schemaFormat: string | undefined } + export interface AsyncAPISchemaDefinition extends SpecificationExtensions { $id?: string; $schema?: JSONSchema7Version; diff --git a/src/utils.ts b/src/utils.ts index cc394c3f7..0690d02f2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -144,21 +144,6 @@ export function findSubArrayIndex(arr: Array, subarr: Array, fromIndex return -1; } -export function retrievePossibleRef(data: any, pathOfData: string, spec: any = {}): any { - if (!hasRef(data)) { - return data; - } - - const refPath = serializePath(data.$ref); - if (pathOfData.startsWith(refPath)) { // starts by given path - return retrieveDeepData(spec, splitPath(refPath)) || data; - } else if (pathOfData.includes(refPath)) { // circular path in substring of path - const substringPath = pathOfData.split(refPath)[0]; - return retrieveDeepData(spec, splitPath(`${substringPath}${refPath}`)) || data; - } - return data; -} - export function resolveServerUrl(url: string): { host: string, pathname: string | undefined } { // eslint-disable-next-line prefer-const let [maybeProtocol, maybeHost] = url.split('://'); diff --git a/test/models/v2/message.spec.ts b/test/models/v2/message.spec.ts index 111031476..ab445a87e 100644 --- a/test/models/v2/message.spec.ts +++ b/test/models/v2/message.spec.ts @@ -26,16 +26,18 @@ describe('Message model', function() { }); }); - describe('.schemaFormat()', function() { + describe('.schemaFormat() + .hasSchemaFormat()', function() { it('should return defined schemaFormat', function() { const doc = { schemaFormat: 'customSchemaFormat' }; const d = new Message(doc, { asyncapi: {} as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeTruthy(); expect(d.schemaFormat()).toEqual('customSchemaFormat'); }); it('should return default schemaFormat if schemaFormat field is absent', function() { const doc = {}; const d = new Message(doc, { asyncapi: { semver: { version: '2.0.0' } } as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeTruthy(); expect(d.schemaFormat()).toEqual('application/vnd.aai.asyncapi;version=2.0.0'); }); }); diff --git a/test/models/v2/schema.spec.ts b/test/models/v2/schema.spec.ts index 4e48b3366..391305bee 100644 --- a/test/models/v2/schema.spec.ts +++ b/test/models/v2/schema.spec.ts @@ -3,6 +3,8 @@ import { Schema } from '../../../src/models/v2/schema'; import { assertExtensions, assertExternalDocumentation } from './utils'; import { xParserSchemaId } from '../../../src/constants'; +import { getDefaultSchemaFormat } from '../../../src/schema-parser'; + import type { v2 } from '../../../src/spec-types'; describe('Channel model', function() { @@ -751,6 +753,20 @@ describe('Channel model', function() { }); }); + describe('.schemaFormat()', function() { + it('should return the format of the schema', function() { + const actualSchemaFormat = 'application/vnd.apache.avro;version=1.9.0'; + const d = new Schema({}, {asyncapi: {} as any, pointer: '', schemaFormat: actualSchemaFormat}); + expect(d.schemaFormat()).toEqual(actualSchemaFormat); + }); + + it('should return the default schema format where there is no value', function() { + const doc = {asyncapi: '2.6.0' }; + const d = new Schema(doc, {asyncapi: { semver: { version: doc.asyncapi } }}); + expect(d.schemaFormat()).toEqual(getDefaultSchemaFormat(doc.asyncapi)); + }); + }); + describe('.then()', function() { it('should return value', function() { const doc = { then: {} }; diff --git a/test/models/v3/message-trait.spec.ts b/test/models/v3/message-trait.spec.ts index 43dce32b6..ad046af22 100644 --- a/test/models/v3/message-trait.spec.ts +++ b/test/models/v3/message-trait.spec.ts @@ -22,16 +22,18 @@ describe('MessageTrait model', function() { }); describe('.schemaFormat()', function() { - it('should return defined schemaFormat', function() { + it('should return always undefined', function() { const doc = { schemaFormat: 'customSchemaFormat' }; const d = new MessageTrait(doc, { asyncapi: {} as any, pointer: '', id: 'message' }); - expect(d.schemaFormat()).toEqual('customSchemaFormat'); + expect(d.schemaFormat()).toBeUndefined(); }); + }); - it('should return default schemaFormat if schemaFormat field is absent', function() { - const doc = {}; - const d = new MessageTrait(doc, { asyncapi: { semver: { version: '2.0.0' } } as any, pointer: '', id: 'message' }); - expect(d.schemaFormat()).toEqual('application/vnd.aai.asyncapi;version=2.0.0'); + describe('.hasSchemaFormat()', function() { + it('should return always false', function() { + const doc = { schemaFormat: 'customSchemaFormat' }; + const d = new MessageTrait(doc, { asyncapi: {} as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeFalsy(); }); }); diff --git a/test/models/v3/message.spec.ts b/test/models/v3/message.spec.ts index 23594952c..91432f36d 100644 --- a/test/models/v3/message.spec.ts +++ b/test/models/v3/message.spec.ts @@ -1,13 +1,7 @@ -import { Channels } from '../../../src/models/v3/channels'; -import { Channel } from '../../../src/models/v3/channel'; import { Message } from '../../../src/models/v3/message'; import { MessageTraits } from '../../../src/models/v3/message-traits'; import { MessageTrait } from '../../../src/models/v3/message-trait'; -import { Operations } from '../../../src/models/v3/operations'; -import { Operation } from '../../../src/models/v3/operation'; import { Schema } from '../../../src/models/v3/schema'; -import { Servers } from '../../../src/models/v3/servers'; -import { Server } from '../../../src/models/v3/server'; import { assertCoreModel } from './utils'; @@ -26,18 +20,27 @@ describe('Message model', function() { }); }); - describe('.schemaFormat()', function() { - it('should return defined schemaFormat', function() { - const doc = { schemaFormat: 'customSchemaFormat' }; + describe('.schemaFormat() + .hasSchemaFormat()', function() { + it('should return defined schemaFormat, and true for hasSchemaFormat()', function() { + const doc = { payload: {schemaFormat: 'customSchemaFormat', schema: {} }}; const d = new Message(doc, { asyncapi: {} as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeTruthy(); expect(d.schemaFormat()).toEqual('customSchemaFormat'); }); it('should return default schemaFormat if schemaFormat field is absent', function() { - const doc = {}; + const doc = {payload: {}}; const d = new Message(doc, { asyncapi: { semver: { version: '2.0.0' } } as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeTruthy(); expect(d.schemaFormat()).toEqual('application/vnd.aai.asyncapi;version=2.0.0'); }); + + it('should return undefined schemaFormat, and false for hasSchemaFormat() if there is no payload', function() { + const doc = {}; + const d = new Message(doc, { asyncapi: { semver: { version: '2.0.0' } } as any, pointer: '', id: 'message' }); + expect(d.hasSchemaFormat()).toBeFalsy(); + expect(d.schemaFormat()).toBeUndefined(); + }); }); describe('.hasPayload()', function() { diff --git a/test/models/v3/schema.spec.ts b/test/models/v3/schema.spec.ts index 59a24050c..f491ea55e 100644 --- a/test/models/v3/schema.spec.ts +++ b/test/models/v3/schema.spec.ts @@ -2,6 +2,7 @@ import { Schema } from '../../../src/models/v3/schema'; import { assertExtensions, assertExternalDocumentation } from './utils'; import { xParserSchemaId } from '../../../src/constants'; +import { getDefaultSchemaFormat } from '../../../src/schema-parser'; import type { v3 } from '../../../src/spec-types'; @@ -732,6 +733,29 @@ describe('Schema model', function() { }); }); + describe('.schemaFormat()', function() { + it('should return the format of the schema', function() { + const actualSchemaFormat = 'application/vnd.apache.avro;version=1.9.0'; + const doc = {schemaFormat: actualSchemaFormat, schema: {}}; + + const d = new Schema(doc, {asyncapi: {} as any, pointer: '', }); + expect(d.schemaFormat()).toEqual(actualSchemaFormat); + }); + + it('should return the default schema format where there is no value', function() { + const d = new Schema({}, {asyncapi: { semver: { version: '3.0.0' } }}); + expect(d.schemaFormat()).toEqual(getDefaultSchemaFormat('3.0.0')); + }); + }); + + describe('MultiFormatSchema is parsed correctly', function() { + it('should return any field from the underlaying schema', function() { + const doc = {schemaFormat: 'whatever', schema: {$comment: 'test'}}; + const d = new Schema(doc, {asyncapi: {} as any, pointer: '', }); + expect(d.$comment()).toEqual('test'); + }); + }); + describe('.then()', function() { it('should return value', function() { const doc = { then: {} };