Skip to content

Commit

Permalink
feat(ns-openapi-2): add support for Example Object (#3247)
Browse files Browse the repository at this point in the history
Refs #3097
  • Loading branch information
char0n authored Oct 10, 2023
1 parent 7adf623 commit e9d1e3e
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 68 deletions.
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 @@ -197,7 +197,7 @@ Only fully implemented specification objects should be checked here.
- [ ] [Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-responses-object)
- [ ] [Response Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-response-object)
- [ ] [Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-headers-object)
- [ ] [Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-example-object)
- [x] [Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-example-object)
- [ ] [Header Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-header-object)
- [x] [Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-tag-object)
- [ ] [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-reference-object)
Expand Down
10 changes: 10 additions & 0 deletions packages/apidom-ns-openapi-2/src/elements/Example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ObjectElement, Attributes, Meta } from '@swagger-api/apidom-core';

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

export default Example;
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 @@ -24,6 +24,7 @@ export {
isLicenseElement,
isContactElement,
isExternalDocumentationElement,
isExampleElement,
isTagElement,
isXmlElement,
isSecurityDefinitionsElement,
Expand All @@ -40,6 +41,7 @@ export {
LicenseElement,
ContactElement,
ExternalDocumentationElement,
ExampleElement,
TagElement,
XmlElement,
SecurityDefinitionsElement,
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
Expand Up @@ -4,6 +4,7 @@ import InfoElement from './elements/Info';
import LicenseElement from './elements/License';
import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
import ExampleElement from './elements/Example';
import TagElement from './elements/Tag';
import XmlElement from './elements/Xml';
import SecurityDefinitionsElement from './elements/SecurityDefinitions';
Expand All @@ -19,6 +20,7 @@ const openApi2 = {
base.register('license', LicenseElement);
base.register('contact', ContactElement);
base.register('externalDocumentation', ExternalDocumentation);
base.register('example', ExampleElement);
base.register('tag', TagElement);
base.register('xml', XmlElement);
base.register('securityDefinitions', SecurityDefinitionsElement);
Expand Down
31 changes: 21 additions & 10 deletions packages/apidom-ns-openapi-2/src/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,14 @@ import InfoElement from './elements/Info';
import LicenseElement from './elements/License';
import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
import ExampleElement from './elements/Example';
import TagElement from './elements/Tag';
import XmlElement from './elements/Xml';
import SecurityDefinitionsElement from './elements/SecurityDefinitions';
import SecuritySchemeElement from './elements/SecurityScheme';
import SecurityRequirementElement from './elements/SecurityRequirement';
import ScopesElement from './elements/Scopes';

export const isTagElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
element instanceof TagElement ||
(hasBasicElementProps(element) &&
isElementType('tag', element) &&
primitiveEq('object', element));
},
);

export const isInfoElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
Expand Down Expand Up @@ -60,6 +51,26 @@ export const isExternalDocumentationElement = createPredicate(
},
);

export const isExampleElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
element instanceof ExampleElement ||
(hasBasicElementProps(element) &&
isElementType('example', element) &&
primitiveEq('object', element));
},
);

export const isTagElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
element instanceof TagElement ||
(hasBasicElementProps(element) &&
isElementType('tag', element) &&
primitiveEq('object', element));
},
);

export const isXmlElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
Expand Down
9 changes: 9 additions & 0 deletions packages/apidom-ns-openapi-2/src/refractor/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import InfoElement from '../elements/Info';
import LicenseElement from '../elements/License';
import ContactElement from '../elements/Contact';
import ExternalDocumentationElement from '../elements/ExternalDocumentation';
import ExampleElement from '../elements/Example';
import TagElement from '../elements/Tag';
import XmlElement from '../elements/Xml';
import SecurityDefinitionsElement from '../elements/SecurityDefinitions';
Expand Down Expand Up @@ -33,6 +34,13 @@ ExternalDocumentationElement.refract = createRefractor([
'ExternalDocumentation',
'$visitor',
]);
ExampleElement.refract = createRefractor([
'visitors',
'document',
'objects',
'Example',
'$visitor',
]);
TagElement.refract = createRefractor(['visitors', 'document', 'objects', 'Tag', '$visitor']);
XmlElement.refract = createRefractor(['visitors', 'document', 'objects', 'XML', '$visitor']);
SecurityDefinitionsElement.refract = createRefractor([
Expand Down Expand Up @@ -63,6 +71,7 @@ export {
LicenseElement,
ContactElement,
ExternalDocumentationElement,
ExampleElement,
TagElement,
XmlElement,
SecurityDefinitionsElement,
Expand Down
4 changes: 4 additions & 0 deletions packages/apidom-ns-openapi-2/src/refractor/specification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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';
import ExampleVisitor from './visitors/open-api-2/example';
import TagVisitor from './visitors/open-api-2/tag';
import XmlVisitor from './visitors/open-api-2/xml';
import SecurityDefinitionsVisitor from './visitors/open-api-2/security-definitions';
Expand Down Expand Up @@ -63,6 +64,9 @@ const specification = {
url: FallbackVisitor,
},
},
Example: {
$visitor: ExampleVisitor,
},
Tag: {
$visitor: TagVisitor,
fixedFields: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import stampit from 'stampit';
import { always } from 'ramda';

import ExampleElement from '../../../../elements/Example';
import FallbackVisitor from '../../FallbackVisitor';
import FixedFieldsVisitor from '../../generics/FixedFieldsVisitor';

const ExampleVisitor = stampit(FixedFieldsVisitor, FallbackVisitor, {
props: {
specPath: always(['document', 'objects', 'Example']),
canSupportSpecificationExtensions: false,
},
init() {
this.element = new ExampleElement();
},
});

export default ExampleVisitor;
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 @@ -23,6 +23,7 @@ export const keyMap = {
LicenseElement: ['content'],
ContactElement: ['content'],
ExternalDocumentationElement: ['content'],
ExampleElement: ['content'],
TagElement: ['content'],
XmlElement: ['content'],
SecurityDefinitionsElement: ['content'],
Expand Down
173 changes: 116 additions & 57 deletions packages/apidom-ns-openapi-2/test/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
LicenseElement,
ContactElement,
ExternalDocumentationElement,
ExampleElement,
TagElement,
XmlElement,
SecurityDefinitionsElement,
Expand All @@ -15,6 +16,7 @@ import {
isLicenseElement,
isContactElement,
isExternalDocumentationElement,
isExampleElement,
isTagElement,
isXmlElement,
isSecurityDefinitionsElement,
Expand All @@ -24,63 +26,6 @@ import {
} from '../src';

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

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

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

assert.isTrue(isTagElement(new TagSubElement()));
});
});

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

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

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

assert.isTrue(isTagElement(tagElementDuck));
assert.isFalse(isTagElement(tagElementSwan));
});
});

context('isInfoElement', function () {
context('given InfoElement instance value', function () {
specify('should return true', function () {
Expand Down Expand Up @@ -309,6 +254,120 @@ describe('predicates', function () {
});
});

context('isExampleElement', function () {
context('given ExampleElement instance value', function () {
specify('should return true', function () {
const element = new ExampleElement();

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

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

assert.isTrue(isExampleElement(new ExampleSubElement()));
});
});

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

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

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

assert.isTrue(isExampleElement(exampleElementDuck));
assert.isFalse(isExampleElement(exampleElementSwan));
});
});

context('isTagElement', function () {
context('given TagElement instance value', function () {
specify('should return true', function () {
const element = new TagElement();

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

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

assert.isTrue(isTagElement(new TagSubElement()));
});
});

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

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

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

assert.isTrue(isTagElement(tagElementDuck));
assert.isFalse(isTagElement(tagElementSwan));
});
});

context('isXmlElement', function () {
context('given XmlElement instance value', function () {
specify('should return true', function () {
Expand Down
Loading

0 comments on commit e9d1e3e

Please sign in to comment.