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 Headers Object #3256

Merged
merged 2 commits into from
Oct 12, 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 @@ -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();
});
});
});
});