Skip to content

Commit

Permalink
feat(ns-openapi-2): add support for Headers Object (#3256)
Browse files Browse the repository at this point in the history
Refs #3097
  • Loading branch information
char0n committed Oct 12, 2023
1 parent 7c4f69e commit a756102
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 17 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 @@ -196,7 +196,7 @@ Only fully implemented specification objects should be checked here.
- [x] [Items Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-items-object)
- [ ] [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)
- [x] [Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-headers-object)
- [x] [Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-example-object)
- [x] [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)
Expand Down
10 changes: 10 additions & 0 deletions packages/apidom-ns-openapi-2/src/elements/Headers.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 Headers extends ObjectElement {
constructor(content?: Record<string, unknown>, meta?: Meta, attributes?: Attributes) {
super(content, meta, attributes);
this.element = 'headers';
}
}

export default Headers;
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 @@ -25,6 +25,7 @@ export {
isContactElement,
isExternalDocumentationElement,
isItemsElement,
isHeadersElement,
isExampleElement,
isHeaderElement,
isTagElement,
Expand All @@ -44,6 +45,7 @@ export {
ContactElement,
ExternalDocumentationElement,
ItemsElement,
HeadersElement,
ExampleElement,
HeaderElement,
TagElement,
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 @@ -6,6 +6,7 @@ import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
import ItemsElement from './elements/Items';
import ExampleElement from './elements/Example';
import HeadersElement from './elements/Headers';
import HeaderElement from './elements/Header';
import TagElement from './elements/Tag';
import XmlElement from './elements/Xml';
Expand All @@ -23,6 +24,7 @@ const openApi2 = {
base.register('contact', ContactElement);
base.register('externalDocumentation', ExternalDocumentation);
base.register('items', ItemsElement);
base.register('headers', HeadersElement);
base.register('example', ExampleElement);
base.register('header', HeaderElement);
base.register('tag', TagElement);
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
Expand Up @@ -6,6 +6,7 @@ import ContactElement from './elements/Contact';
import ExternalDocumentation from './elements/ExternalDocumentation';
import ItemsElement from './elements/Items';
import ExampleElement from './elements/Example';
import HeadersElement from './elements/Headers';
import HeaderElement from './elements/Header';
import TagElement from './elements/Tag';
import XmlElement from './elements/Xml';
Expand Down Expand Up @@ -63,6 +64,16 @@ export const isItemsElement = createPredicate(
},
);

export const isHeadersElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: unknown) =>
element instanceof HeadersElement ||
(hasBasicElementProps(element) &&
isElementType('headers', element) &&
primitiveEq('object', element));
},
);

export const isExampleElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: unknown) =>
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 @@ -3,6 +3,7 @@ import LicenseElement from '../elements/License';
import ContactElement from '../elements/Contact';
import ExternalDocumentationElement from '../elements/ExternalDocumentation';
import ItemsElement from '../elements/Items';
import HeadersElement from '../elements/Headers';
import ExampleElement from '../elements/Example';
import HeaderElement from '../elements/Header';
import TagElement from '../elements/Tag';
Expand Down Expand Up @@ -37,6 +38,13 @@ ExternalDocumentationElement.refract = createRefractor([
'$visitor',
]);
ItemsElement.refract = createRefractor(['visitors', 'document', 'objects', 'Items', '$visitor']);
HeadersElement.refract = createRefractor([
'visitors',
'document',
'objects',
'Headers',
'$visitor',
]);
ExampleElement.refract = createRefractor([
'visitors',
'document',
Expand Down Expand Up @@ -76,6 +84,7 @@ export {
ContactElement,
ExternalDocumentationElement,
ItemsElement,
HeadersElement,
ExampleElement,
HeaderElement,
TagElement,
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 @@ -7,6 +7,7 @@ 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 ItemsVisitor from './visitors/open-api-2/items';
import HeadersVisitor from './visitors/open-api-2/headers';
import ExampleVisitor from './visitors/open-api-2/example';
import HeaderVisitor from './visitors/open-api-2/header';
import TagVisitor from './visitors/open-api-2/tag';
Expand Down Expand Up @@ -95,6 +96,9 @@ const specification = {
multipleOf: jsonSchemaFixedFields.multipleOf,
},
},
Headers: {
$visitor: HeadersVisitor,
},
Example: {
$visitor: ExampleVisitor,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { always } from 'ramda';

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

const ExampleVisitor = stampit(FixedFieldsVisitor, FallbackVisitor, {
const ExampleVisitor = stampit(MapVisitor, FallbackVisitor, {
props: {
specPath: always(['document', 'objects', 'Example']),
specPath: always(['value']),
canSupportSpecificationExtensions: false,
},
init() {
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 MapVisitor from '../../generics/MapVisitor';
import FallbackVisitor from '../../FallbackVisitor';
import HeadersElement from '../../../../elements/Headers';

const HeadersVisitor = stampit(MapVisitor, FallbackVisitor, {
props: {
specPath: always(['document', 'objects', 'Header']),
canSupportSpecificationExtensions: false,
},
init() {
this.element = new HeadersElement();
},
});

export default HeadersVisitor;
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 @@ -25,6 +25,7 @@ export const keyMap = {
ExternalDocumentationElement: ['content'],
ItemsElement: ['content'],
ExampleElement: ['content'],
HeadersElement: ['content'],
HeaderElement: ['content'],
TagElement: ['content'],
XmlElement: ['content'],
Expand Down
71 changes: 58 additions & 13 deletions packages/apidom-ns-openapi-2/test/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ContactElement,
ExternalDocumentationElement,
ItemsElement,
HeadersElement,
ExampleElement,
HeaderElement,
TagElement,
Expand All @@ -19,6 +20,7 @@ import {
isContactElement,
isExternalDocumentationElement,
isItemsElement,
isHeadersElement,
isExampleElement,
isHeaderElement,
isTagElement,
Expand All @@ -41,7 +43,6 @@ describe('predicates', function () {

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()));
Expand Down Expand Up @@ -98,7 +99,6 @@ describe('predicates', function () {

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()));
Expand Down Expand Up @@ -155,7 +155,6 @@ describe('predicates', function () {

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

assert.isTrue(isContactElement(new ContactSubElement()));
Expand Down Expand Up @@ -212,7 +211,6 @@ describe('predicates', function () {

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

assert.isTrue(isExternalDocumentationElement(new ExternalDocumentationSubElement()));
Expand Down Expand Up @@ -269,7 +267,6 @@ describe('predicates', function () {

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

assert.isTrue(isItemsElement(new ItemsSubElement()));
Expand Down Expand Up @@ -315,6 +312,62 @@ describe('predicates', function () {
});
});

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

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

context('given subtype instance value', function () {
specify('should return true', function () {
class HeadersSubElement extends HeadersElement {}

assert.isTrue(isHeadersElement(new HeadersSubElement()));
});
});

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

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

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

assert.isTrue(isHeadersElement(headersElementDuck));
assert.isFalse(isHeadersElement(headersElementSwan));
});
});

context('isExampleElement', function () {
context('given ExampleElement instance value', function () {
specify('should return true', function () {
Expand All @@ -326,7 +379,6 @@ describe('predicates', function () {

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()));
Expand Down Expand Up @@ -383,7 +435,6 @@ describe('predicates', function () {

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

assert.isTrue(isHeaderElement(new HeaderSubElement()));
Expand Down Expand Up @@ -440,7 +491,6 @@ describe('predicates', function () {

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()));
Expand Down Expand Up @@ -497,7 +547,6 @@ describe('predicates', function () {

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

assert.isTrue(isXmlElement(new XmlSubElement()));
Expand Down Expand Up @@ -554,7 +603,6 @@ describe('predicates', function () {

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

assert.isTrue(isSecurityDefinitionsElement(new SecurityDefinitionsSubElement()));
Expand Down Expand Up @@ -611,7 +659,6 @@ describe('predicates', function () {

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

assert.isTrue(isSecuritySchemeElement(new SecuritySchemeSubElement()));
Expand Down Expand Up @@ -668,7 +715,6 @@ describe('predicates', function () {

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

assert.isTrue(isScopesElement(new ScopesSubElement()));
Expand Down Expand Up @@ -725,7 +771,6 @@ describe('predicates', function () {

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

assert.isTrue(isSecurityRequirementElement(new SecurityRequirementSubElement()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`refractor elements HeadersElement should refract to semantic ApiDOM tree 1`] = `
(HeadersElement
(MemberElement
(StringElement)
(HeaderElement))
(MemberElement
(StringElement)
(HeaderElement)))
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect } from 'chai';
import { sexprs } from '@swagger-api/apidom-core';

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

describe('refractor', function () {
context('elements', function () {
context('HeadersElement', function () {
specify('should refract to semantic ApiDOM tree', function () {
const headersElement = HeadersElement.refract({
header1: {},
header2: {},
});

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

0 comments on commit a756102

Please sign in to comment.