diff --git a/packages/apidom-ns-openapi-2/README.md b/packages/apidom-ns-openapi-2/README.md index ae823d532d..215ae5fdf8 100644 --- a/packages/apidom-ns-openapi-2/README.md +++ b/packages/apidom-ns-openapi-2/README.md @@ -187,7 +187,7 @@ Only fully implemented specification objects should be checked here. - [ ] [Swagger Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-swagger-object) - [ ] [Info Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-info-object) - [x] [Contact Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-contact-object) -- [ ] [License Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-license-object) +- [x] [License Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-license-object) - [ ] [Paths Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-paths-object) - [ ] [Path Item Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-path-item-object) - [ ] [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-operation-object) diff --git a/packages/apidom-ns-openapi-2/src/elements/License.ts b/packages/apidom-ns-openapi-2/src/elements/License.ts new file mode 100644 index 0000000000..8f8bae0d2d --- /dev/null +++ b/packages/apidom-ns-openapi-2/src/elements/License.ts @@ -0,0 +1,26 @@ +import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core'; + +class License extends ObjectElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'license'; + } + + get name(): StringElement | undefined { + return this.get('name'); + } + + set name(name: StringElement | undefined) { + this.set('name', name); + } + + get url(): StringElement | undefined { + return this.get('url'); + } + + set url(url: StringElement | undefined) { + this.set('url', url); + } +} + +export default License; diff --git a/packages/apidom-ns-openapi-2/src/index.ts b/packages/apidom-ns-openapi-2/src/index.ts index 9beb06bd11..40a9478e6c 100644 --- a/packages/apidom-ns-openapi-2/src/index.ts +++ b/packages/apidom-ns-openapi-2/src/index.ts @@ -20,6 +20,7 @@ export { default as refract, createRefractor } from './refractor'; export { default as specificationObj } from './refractor/specification'; export { + isLicenseElement, isContactElement, isExternalDocumentationElement, isXmlElement, @@ -33,6 +34,7 @@ export { keyMap, getNodeType } from './traversal/visitor'; // OpenAPI 2.0 elements export { + LicenseElement, ContactElement, ExternalDocumentationElement, XmlElement, diff --git a/packages/apidom-ns-openapi-2/src/namespace.ts b/packages/apidom-ns-openapi-2/src/namespace.ts index 2c772df40b..16cacfc4ba 100644 --- a/packages/apidom-ns-openapi-2/src/namespace.ts +++ b/packages/apidom-ns-openapi-2/src/namespace.ts @@ -1,5 +1,6 @@ import { NamespacePluginOptions } from '@swagger-api/apidom-core'; +import LicenseElement from './elements/License'; import ContactElement from './elements/Contact'; import ExternalDocumentation from './elements/ExternalDocumentation'; import XmlElement from './elements/Xml'; @@ -12,6 +13,7 @@ const openApi2 = { namespace: (options: NamespacePluginOptions) => { const { base } = options; + base.register('license', LicenseElement); base.register('contact', ContactElement); base.register('externalDocumentation', ExternalDocumentation); base.register('xml', XmlElement); diff --git a/packages/apidom-ns-openapi-2/src/predicates.ts b/packages/apidom-ns-openapi-2/src/predicates.ts index daf881ce0b..8909c47474 100644 --- a/packages/apidom-ns-openapi-2/src/predicates.ts +++ b/packages/apidom-ns-openapi-2/src/predicates.ts @@ -1,5 +1,6 @@ import { createPredicate } from '@swagger-api/apidom-core'; +import LicenseElement from './elements/License'; import ContactElement from './elements/Contact'; import ExternalDocumentation from './elements/ExternalDocumentation'; import XmlElement from './elements/Xml'; @@ -8,6 +9,16 @@ import SecuritySchemeElement from './elements/SecurityScheme'; import SecurityRequirementElement from './elements/SecurityRequirement'; import ScopesElement from './elements/Scopes'; +export const isLicenseElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq }) => { + return (element: any) => + element instanceof LicenseElement || + (hasBasicElementProps(element) && + isElementType('license', element) && + primitiveEq('object', element)); + }, +); + export const isContactElement = createPredicate( ({ hasBasicElementProps, isElementType, primitiveEq }) => { return (element: any) => diff --git a/packages/apidom-ns-openapi-2/src/refractor/registration.ts b/packages/apidom-ns-openapi-2/src/refractor/registration.ts index 0380cb196e..abf1cf9125 100644 --- a/packages/apidom-ns-openapi-2/src/refractor/registration.ts +++ b/packages/apidom-ns-openapi-2/src/refractor/registration.ts @@ -1,3 +1,4 @@ +import LicenseElement from '../elements/License'; import ContactElement from '../elements/Contact'; import ExternalDocumentationElement from '../elements/ExternalDocumentation'; import XmlElement from '../elements/Xml'; @@ -8,6 +9,13 @@ import SecurityRequirementElement from '../elements/SecurityRequirement'; import { createRefractor } from './index'; // register refractors specific to element types +LicenseElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'License', + '$visitor', +]); ContactElement.refract = createRefractor([ 'visitors', 'document', @@ -47,6 +55,7 @@ SecurityRequirementElement.refract = createRefractor([ ]); export { + LicenseElement, ContactElement, ExternalDocumentationElement, XmlElement, diff --git a/packages/apidom-ns-openapi-2/src/refractor/specification.ts b/packages/apidom-ns-openapi-2/src/refractor/specification.ts index 66ce32c52b..a5e00053bc 100644 --- a/packages/apidom-ns-openapi-2/src/refractor/specification.ts +++ b/packages/apidom-ns-openapi-2/src/refractor/specification.ts @@ -1,4 +1,5 @@ import FallbackVisitor from './visitors/FallbackVisitor'; +import LicenseVisitor from './visitors/open-api-2/license'; import ContactVisitor from './visitors/open-api-2/contact'; import ExternalDocumentationElement from './visitors/open-api-2/external-documentation'; import XmlVisitor from './visitors/open-api-2/xml'; @@ -22,6 +23,13 @@ const specification = { value: FallbackVisitor, document: { objects: { + License: { + $visitor: LicenseVisitor, + fixedFields: { + name: FallbackVisitor, + url: FallbackVisitor, + }, + }, Contact: { $visitor: ContactVisitor, fixedFields: { diff --git a/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/license/index.ts b/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/license/index.ts new file mode 100644 index 0000000000..c2d058fc53 --- /dev/null +++ b/packages/apidom-ns-openapi-2/src/refractor/visitors/open-api-2/license/index.ts @@ -0,0 +1,18 @@ +import stampit from 'stampit'; +import { always } from 'ramda'; + +import LicenseElement from '../../../../elements/License'; +import FallbackVisitor from '../../FallbackVisitor'; +import FixedFieldsVisitor from '../../generics/FixedFieldsVisitor'; + +const LicenseVisitor = stampit(FixedFieldsVisitor, FallbackVisitor, { + props: { + specPath: always(['document', 'objects', 'License']), + canSupportSpecificationExtensions: true, + }, + init() { + this.element = new LicenseElement(); + }, +}); + +export default LicenseVisitor; diff --git a/packages/apidom-ns-openapi-2/src/traversal/visitor.ts b/packages/apidom-ns-openapi-2/src/traversal/visitor.ts index eac54072c9..5e2b166026 100644 --- a/packages/apidom-ns-openapi-2/src/traversal/visitor.ts +++ b/packages/apidom-ns-openapi-2/src/traversal/visitor.ts @@ -19,6 +19,7 @@ export const getNodeType = (element: T): string | undefined = */ export const keyMap = { + LicenseElement: ['content'], ContactElement: ['content'], ExternalDocumentationElement: ['content'], XmlElement: ['content'], diff --git a/packages/apidom-ns-openapi-2/test/predicates.ts b/packages/apidom-ns-openapi-2/test/predicates.ts index ada90e5555..04405212ac 100644 --- a/packages/apidom-ns-openapi-2/test/predicates.ts +++ b/packages/apidom-ns-openapi-2/test/predicates.ts @@ -1,6 +1,7 @@ import { assert } from 'chai'; import { + LicenseElement, ContactElement, ExternalDocumentationElement, XmlElement, @@ -8,6 +9,7 @@ import { SecuritySchemeElement, ScopesElement, SecurityRequirementElement, + isLicenseElement, isContactElement, isExternalDocumentationElement, isXmlElement, @@ -18,6 +20,63 @@ import { } from '../src'; describe('predicates', function () { + context('isLicenseElement', function () { + context('given LicenseElement instance value', function () { + specify('should return true', function () { + const element = new LicenseElement(); + + assert.isTrue(isLicenseElement(element)); + }); + }); + + context('given subtype instance value', function () { + specify('should return true', function () { + // eslint-disable-next-line @typescript-eslint/naming-convention + class LicenseSubElement extends LicenseElement {} + + assert.isTrue(isLicenseElement(new LicenseSubElement())); + }); + }); + + context('given non LicenseSubElement instance value', function () { + specify('should return false', function () { + assert.isFalse(isLicenseElement(1)); + assert.isFalse(isLicenseElement(null)); + assert.isFalse(isLicenseElement(undefined)); + assert.isFalse(isLicenseElement({})); + assert.isFalse(isLicenseElement([])); + assert.isFalse(isLicenseElement('string')); + }); + }); + + specify('should support duck-typing', function () { + const licenseElementDuck = { + _storedElement: 'license', + _content: [], + primitive() { + return 'object'; + }, + get element() { + return this._storedElement; + }, + }; + + const licenseElementSwan = { + _storedElement: undefined, + _content: undefined, + primitive() { + return 'swan'; + }, + get length() { + return 0; + }, + }; + + assert.isTrue(isLicenseElement(licenseElementDuck)); + assert.isFalse(isLicenseElement(licenseElementSwan)); + }); + }); + context('isContactElement', function () { context('given ContactElement instance value', function () { specify('should return true', function () { diff --git a/packages/apidom-ns-openapi-2/test/refractor/elements/License/__snapshots__/index.ts.snap b/packages/apidom-ns-openapi-2/test/refractor/elements/License/__snapshots__/index.ts.snap new file mode 100644 index 0000000000..7118444fb3 --- /dev/null +++ b/packages/apidom-ns-openapi-2/test/refractor/elements/License/__snapshots__/index.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements LicenseElement should refract to semantic ApiDOM tree 1`] = ` +(LicenseElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))) +`; diff --git a/packages/apidom-ns-openapi-2/test/refractor/elements/License/index.ts b/packages/apidom-ns-openapi-2/test/refractor/elements/License/index.ts new file mode 100644 index 0000000000..5c57a02423 --- /dev/null +++ b/packages/apidom-ns-openapi-2/test/refractor/elements/License/index.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { LicenseElement } from '../../../../src'; + +describe('refractor', function () { + context('elements', function () { + context('LicenseElement', function () { + specify('should refract to semantic ApiDOM tree', function () { + const licenseElement = LicenseElement.refract({ + name: 'Apache 2.0', + url: 'http://www.apache.org/licenses/LICENSE-2.0.html', + }); + + expect(sexprs(licenseElement)).toMatchSnapshot(); + }); + }); + }); +});