Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ns-openapi-2): add support for Info Object #3245

Merged
merged 1 commit into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/apidom-ns-openapi-2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const swaggerElement = SwaggerElement.refract(apiDOM.result, {
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] [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)
- [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)
Expand Down
62 changes: 62 additions & 0 deletions packages/apidom-ns-openapi-2/src/elements/Info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { StringElement, ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core';

import ContactElement from './Contact';
import LicenseElement from './License';

class Info extends ObjectElement {
constructor(content?: Record<string, unknown>, meta?: Meta, attributes?: Attributes) {
super(content, meta, attributes);
this.element = 'info';
this.classes.push('info');
}

get title(): StringElement | undefined {
return this.get('title');
}

set title(title: StringElement | undefined) {
this.set('title', title);
}

get description(): StringElement | undefined {
return this.get('description');
}

set description(description: StringElement | undefined) {
this.set('description', description);
}

get termsOfService(): StringElement | undefined {
return this.get('termsOfService');
}

set termsOfService(tos: StringElement | undefined) {
this.set('termsOfService', tos);
}

get contact(): ContactElement | undefined {
return this.get('contact');
}

set contact(contactElement: ContactElement | undefined) {
this.set('contact', contactElement);
}

get license(): LicenseElement | undefined {
return this.get('license');
}

set license(licenseElement: LicenseElement | undefined) {
this.set('license', licenseElement);
}

get version(): StringElement | undefined {
return this.get('version');
}

set version(version: StringElement | undefined) {
this.set('version', version);
}
}

export default Info;
2 changes: 2 additions & 0 deletions packages/apidom-ns-openapi-2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { default as refract, createRefractor } from './refractor';
export { default as specificationObj } from './refractor/specification';

export {
isInfoElement,
isLicenseElement,
isContactElement,
isExternalDocumentationElement,
Expand All @@ -34,6 +35,7 @@ export { keyMap, getNodeType } from './traversal/visitor';

// OpenAPI 2.0 elements
export {
InfoElement,
LicenseElement,
ContactElement,
ExternalDocumentationElement,
Expand Down
2 changes: 2 additions & 0 deletions packages/apidom-ns-openapi-2/src/namespace.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NamespacePluginOptions } from '@swagger-api/apidom-core';

import InfoElement from './elements/Info';
import LicenseElement from './elements/License';
import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
Expand All @@ -13,6 +14,7 @@ const openApi2 = {
namespace: (options: NamespacePluginOptions) => {
const { base } = options;

base.register('info', InfoElement);
base.register('license', LicenseElement);
base.register('contact', ContactElement);
base.register('externalDocumentation', ExternalDocumentation);
Expand Down
11 changes: 11 additions & 0 deletions packages/apidom-ns-openapi-2/src/predicates.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createPredicate } from '@swagger-api/apidom-core';

import InfoElement from './elements/Info';
import LicenseElement from './elements/License';
import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
Expand All @@ -9,6 +10,16 @@ import SecuritySchemeElement from './elements/SecurityScheme';
import SecurityRequirementElement from './elements/SecurityRequirement';
import ScopesElement from './elements/Scopes';

export const isInfoElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
element instanceof InfoElement ||
(hasBasicElementProps(element) &&
isElementType('info', element) &&
primitiveEq('object', element));
},
);

export const isLicenseElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
Expand Down
3 changes: 3 additions & 0 deletions packages/apidom-ns-openapi-2/src/refractor/registration.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import InfoElement from '../elements/Info';
import LicenseElement from '../elements/License';
import ContactElement from '../elements/Contact';
import ExternalDocumentationElement from '../elements/ExternalDocumentation';
Expand All @@ -9,6 +10,7 @@ import SecurityRequirementElement from '../elements/SecurityRequirement';
import { createRefractor } from './index';

// register refractors specific to element types
InfoElement.refract = createRefractor(['visitors', 'document', 'objects', 'Info', '$visitor']);
LicenseElement.refract = createRefractor([
'visitors',
'document',
Expand Down Expand Up @@ -55,6 +57,7 @@ SecurityRequirementElement.refract = createRefractor([
]);

export {
InfoElement,
LicenseElement,
ContactElement,
ExternalDocumentationElement,
Expand Down
17 changes: 17 additions & 0 deletions packages/apidom-ns-openapi-2/src/refractor/specification.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import FallbackVisitor from './visitors/FallbackVisitor';
import InfoVisitor from './visitors/open-api-2/info';
import InfoVersionVisitor from './visitors/open-api-2/info/VersionVisitor';
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';
Expand All @@ -23,6 +25,21 @@ const specification = {
value: FallbackVisitor,
document: {
objects: {
Info: {
$visitor: InfoVisitor,
fixedFields: {
title: FallbackVisitor,
description: FallbackVisitor,
termsOfService: FallbackVisitor,
contact: {
$ref: '#/visitors/document/objects/Contact',
},
license: {
$ref: '#/visitors/document/objects/License',
},
version: InfoVersionVisitor,
},
},
License: {
$visitor: LicenseVisitor,
fixedFields: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import stampit from 'stampit';
import { StringElement, BREAK, cloneDeep } from '@swagger-api/apidom-core';

import FallbackVisitor from '../../FallbackVisitor';

const VersionVisitor = stampit(FallbackVisitor, {
methods: {
StringElement(stringElement: StringElement) {
this.element = cloneDeep(stringElement);
this.element.classes.push('api-version');
this.element.classes.push('version');

return BREAK;
},
},
});

export default VersionVisitor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import stampit from 'stampit';
import { always } from 'ramda';

import InfoElement from '../../../../elements/Info';
import FallbackVisitor from '../../FallbackVisitor';
import FixedFieldsVisitor from '../../generics/FixedFieldsVisitor';

const InfoVisitor = stampit(FixedFieldsVisitor, FallbackVisitor, {
props: {
specPath: always(['document', 'objects', 'Info']),
canSupportSpecificationExtensions: true,
},
init() {
this.element = new InfoElement();
},
});

export default InfoVisitor;
1 change: 1 addition & 0 deletions packages/apidom-ns-openapi-2/src/traversal/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const getNodeType = <T extends Element>(element: T): string | undefined =
*/

export const keyMap = {
InfoElement: ['content'],
LicenseElement: ['content'],
ContactElement: ['content'],
ExternalDocumentationElement: ['content'],
Expand Down
59 changes: 59 additions & 0 deletions packages/apidom-ns-openapi-2/test/predicates.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assert } from 'chai';

import {
InfoElement,
LicenseElement,
ContactElement,
ExternalDocumentationElement,
Expand All @@ -9,6 +10,7 @@ import {
SecuritySchemeElement,
ScopesElement,
SecurityRequirementElement,
isInfoElement,
isLicenseElement,
isContactElement,
isExternalDocumentationElement,
Expand All @@ -20,6 +22,63 @@ import {
} from '../src';

describe('predicates', function () {
context('isInfoElement', function () {
context('given InfoElement instance value', function () {
specify('should return true', function () {
const element = new InfoElement();

assert.isTrue(isInfoElement(element));
});
});

context('given subtype instance value', function () {
specify('should return true', function () {
// eslint-disable-next-line @typescript-eslint/naming-convention
class InfoSubElement extends InfoElement {}

assert.isTrue(isInfoElement(new InfoSubElement()));
});
});

context('given non InfoSubElement instance value', function () {
specify('should return false', function () {
assert.isFalse(isInfoElement(1));
assert.isFalse(isInfoElement(null));
assert.isFalse(isInfoElement(undefined));
assert.isFalse(isInfoElement({}));
assert.isFalse(isInfoElement([]));
assert.isFalse(isInfoElement('string'));
});
});

specify('should support duck-typing', function () {
const infoElementDuck = {
_storedElement: 'info',
_content: [],
primitive() {
return 'object';
},
get element() {
return this._storedElement;
},
};

const infoElementSwan = {
_storedElement: undefined,
_content: undefined,
primitive() {
return 'swan';
},
get length() {
return 0;
},
};

assert.isTrue(isInfoElement(infoElementDuck));
assert.isFalse(isInfoElement(infoElementSwan));
});
});

context('isLicenseElement', function () {
context('given LicenseElement instance value', function () {
specify('should return true', function () {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`refractor elements InfoElement should refract to semantic ApiDOM tree 1`] = `
(InfoElement
(MemberElement
(StringElement)
(StringElement))
(MemberElement
(StringElement)
(StringElement))
(MemberElement
(StringElement)
(StringElement))
(MemberElement
(StringElement)
(ContactElement))
(MemberElement
(StringElement)
(LicenseElement))
(MemberElement
(StringElement)
(StringElement)))
`;
23 changes: 23 additions & 0 deletions packages/apidom-ns-openapi-2/test/refractor/elements/Info/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { expect } from 'chai';
import { sexprs } from '@swagger-api/apidom-core';

import { InfoElement } from '../../../../src';

describe('refractor', function () {
context('elements', function () {
context('InfoElement', function () {
specify('should refract to semantic ApiDOM tree', function () {
const infoElement = InfoElement.refract({
title: 'Sample Pet Store App',
description: 'This is a sample server for a pet store.',
termsOfService: 'https://example.com/terms/',
contact: {},
license: {},
version: '1.0.1',
});

expect(sexprs(infoElement)).toMatchSnapshot();
});
});
});
});