Skip to content

Commit

Permalink
feat(ns-openapi-2): add support for Security Scheme Object
Browse files Browse the repository at this point in the history
Refs #3097
  • Loading branch information
char0n committed Oct 5, 2023
1 parent 4226eab commit c289e74
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 8 deletions.
4 changes: 2 additions & 2 deletions packages/apidom-ns-openapi-2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ Only fully implemented specification objects should be checked here.
- [ ] [Parameters Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-paramters-definitions-object)
- [ ] [Responses Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-responses-definitions-object)
- [ ] [Security Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-security-definitions-object)
- [ ] [Security Scheme Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-security-scheme-object)
- [x] [Security Scheme Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-security-scheme-object)
- [x] [Scopes Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-scopes-object)
- [x] [Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-security-requirement-object)
- [ ] [Specification extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-specification-extensions)
- [x] [Specification extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#user-content-specification-extensions)

80 changes: 80 additions & 0 deletions packages/apidom-ns-openapi-2/src/elements/SecurityScheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
ObjectElement,
ArrayElement,
StringElement,
Attributes,
Meta,
} from '@swagger-api/apidom-core';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

get scopes(): ArrayElement | undefined {
return this.get('scopes');
}

set scopes(scopes: ArrayElement | undefined) {
this.set('scopes', scopes);
}
}

export default SecurityScheme;
13 changes: 10 additions & 3 deletions packages/apidom-ns-openapi-2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ export { default } from './namespace';
export { default as refract, createRefractor } from './refractor';
export { default as specificationObj } from './refractor/specification';

export { isScopesElement, isSecurityRequirementElement } from './predicates';
export {
isSecuritySchemeElement,
isScopesElement,
isSecurityRequirementElement,
} from './predicates';

export { keyMap, getNodeType } from './traversal/visitor';

// OpenAPI 2.0 elements
export { ScopesElement } from './refractor/registration';
export { SecurityRequirementElement } from './refractor/registration';
export {
SecuritySchemeElement,
ScopesElement,
SecurityRequirementElement,
} from './refractor/registration';
// NCE types
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 SecuritySchemeElement from './elements/SecurityScheme';
import ScopesElement from './elements/ScopesElement';
import SecurityRequirementElement from './elements/SecurityRequirement';

Expand All @@ -9,6 +10,7 @@ const openApi2 = {

base.register('scopes', ScopesElement);
base.register('securityRequirement', SecurityRequirementElement);
base.register('securityScheme', SecuritySchemeElement);

return base;
},
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,8 +1,19 @@
import { createPredicate } from '@swagger-api/apidom-core';

import SecuritySchemeElement from './elements/SecurityScheme';
import SecurityRequirementElement from './elements/SecurityRequirement';
import ScopesElement from './elements/ScopesElement';

export const isSecuritySchemeElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
element instanceof SecuritySchemeElement ||
(hasBasicElementProps(element) &&
isElementType('securityScheme', element) &&
primitiveEq('object', element));
},
);

export const isScopesElement = createPredicate(
({ hasBasicElementProps, isElementType, primitiveEq }) => {
return (element: any) =>
Expand Down
10 changes: 9 additions & 1 deletion packages/apidom-ns-openapi-2/src/refractor/registration.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import SecuritySchemeElement from '../elements/SecurityScheme';
import ScopesElement from '../elements/ScopesElement';
import SecurityRequirementElement from '../elements/SecurityRequirement';
import { createRefractor } from './index';

// register refractors specific to element types
SecuritySchemeElement.refract = createRefractor([
'visitors',
'document',
'objects',
'SecurityScheme',
'$visitor',
]);
ScopesElement.refract = createRefractor(['visitors', 'document', 'objects', 'Scopes', '$visitor']);
SecurityRequirementElement.refract = createRefractor([
'visitors',
Expand All @@ -12,4 +20,4 @@ SecurityRequirementElement.refract = createRefractor([
'$visitor',
]);

export { ScopesElement, SecurityRequirementElement };
export { SecuritySchemeElement, ScopesElement, SecurityRequirementElement };
20 changes: 18 additions & 2 deletions packages/apidom-ns-openapi-2/src/refractor/specification.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FallbackVisitor from './visitors/FallbackVisitor';
import SecuritySchemeVisitor from './visitors/open-api-2/security-scheme';
import ScopesVisitor from './visitors/open-api-2/scopes';
import SecurityRequirementVisitor from './visitors/open-api-2/security-requirement';
import SpecificationExtensionVisitor from './visitors/SpecificationExtensionVisitor';
Expand All @@ -17,12 +18,27 @@ const specification = {
value: FallbackVisitor,
document: {
objects: {
SecurityRequirement: {
$visitor: SecurityRequirementVisitor,
SecurityScheme: {
$visitor: SecuritySchemeVisitor,
fixedFields: {
type: FallbackVisitor,
description: FallbackVisitor,
name: FallbackVisitor,
in: FallbackVisitor,
flow: FallbackVisitor,
authorizationUrl: FallbackVisitor,
token: FallbackVisitor,
scopes: {
$ref: '#/visitors/document/objects/Scopes',
},
},
},
Scopes: {
$visitor: ScopesVisitor,
},
SecurityRequirement: {
$visitor: SecurityRequirementVisitor,
},
},
extension: {
$visitor: SpecificationExtensionVisitor,
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 SecuritySchemeElement from '../../../../elements/SecurityScheme';
import FallbackVisitor from '../../FallbackVisitor';
import FixedFieldsVisitor from '../../generics/FixedFieldsVisitor';

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

export default SecuritySchemeVisitor;
2 changes: 2 additions & 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,8 @@ export const getNodeType = <T extends Element>(element: T): string | undefined =
*/

export const keyMap = {
SecuritySchemeElement: ['content'],
ScopesElement: ['content'],
SecurityRequirementElement: ['content'],
...keyMapBase,
};
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,13 +1,72 @@
import { assert } from 'chai';

import {
SecuritySchemeElement,
ScopesElement,
SecurityRequirementElement,
isSecuritySchemeElement,
isScopesElement,
isSecurityRequirementElement,
} from '../src';

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

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

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()));
});
});

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

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

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

assert.isTrue(isSecuritySchemeElement(securitySchemeElementDuck));
assert.isFalse(isSecuritySchemeElement(securitySchemeElementSwan));
});
});

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

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

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

describe('refractor', function () {
context('elements', function () {
context('SecuritySchemeElement', function () {
specify('should refract to semantic ApiDOM tree', function () {
const securitySchemeElement = SecuritySchemeElement.refract({
type: 'apiKey',
description: 'simple description',
name: 'api_key',
in: 'header',
flow: 'implicit',
authorizationUrl: 'https://swagger.io/api/oauth/dialog',
tokenUrl: 'https://swagger.io/api/oauth/token-url',
scopes: {
'write:pets': 'modify pets in your account',
'read:pets': 'read your pets',
},
});

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

specify('should support specification extensions', function () {
const securitySchemeElement = SecuritySchemeElement.refract({
type: 'apiKey',
'x-extension': 'extension',
}) as SecuritySchemeElement;

assert.isFalse(
includesClasses(['specification-extension'], securitySchemeElement.getMember('type')),
);
assert.isTrue(
includesClasses(
['specification-extension'],
securitySchemeElement.getMember('x-extension'),
),
);
});
});
});
});

0 comments on commit c289e74

Please sign in to comment.