UNPKG

@superhero/oas

Version:

OpenAPI Specification (OAS) router

159 lines (138 loc) 3.92 kB
import assert from 'node:assert' import config from '../config.json' with { type: 'json' } /** * An abstract class that provides common functionality for * OpenAPI Specification components. * * @abstract * @memberof Oas */ export default class ComponentsAbstraction { listFormat = new Intl.ListFormat('en', { style:'long', type:'disjunction' }) deepEqual = assert.deepEqual validComponentAttributes = [] /** * Expected to be overridden with a specific * implementation, through the bootstrap process. */ specification = config.oas conformRef(ref, instance, ...args) { if('string' === typeof ref) { const [ uri, pointer ] = ref.split('#') this.validateRefUri(uri) this.validateRefPointer(pointer) const [ root, ...segments ] = pointer.split('/') this.validateRefRoot(root) const component = this.locateComponentByRef(uri, segments) return this.conform(component, instance, ...args) } else { const error = new TypeError(`Invalid ref type ${Object.prototype.toString.call(ref)}`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = new TypeError('The ref type must be a string') throw error } } locateComponentByRef(uri, path) { try { const component = path .map(this.decodeJsonPointer) .reduce((spec, pointer) => spec[pointer], this.specification) if(component) { return component } else { const error = new Error(`The ref pointer path is empty`) error.code = 'E_OAS_INVALID_SPECIFICATION' throw error } } catch(reason) { const error = new TypeError(`Invalid ref pointer path`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = reason throw error } } // RFC 6901. Section 4. Pointer Evaluation decodeJsonPointer(segment) { return segment.replace(/~1/g, '/').replace(/~0/g, '~') } validateRefUri(uri) { if(true === !!uri) { const error = new TypeError(`Invalid ref uri`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = new TypeError('Only local references are supported') throw error } } validateRefPointer(pointer) { if(false === !!pointer) { const error = new TypeError(`The ref pointer is missing`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = new TypeError('The ref must include a # symbol') throw error } } validateRefRoot(root) { if(true === !!root) { const error = new TypeError(`Invalid ref pointer`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = new TypeError('The ref pointer must be an absolute path') throw error } } validateComponent(component) { const componentType = Object.prototype.toString.call(component) if('[object Object]' !== componentType) { const error = new Error(`Invalid component type ${componentType}`) error.code = 'E_OAS_INVALID_SPECIFICATION' error.cause = new Error('The component type must be an [object Object]') throw error } this.validateComponentAttributes(component) this.validateComponentRef(component) } validateComponentRef(component) { if('$ref' in component) { if(Object.keys(component).length > 1) { const error = new Error('The component must only have the "$ref" attribute when present') error.code = 'E_OAS_INVALID_SPECIFICATION' throw error } } } validateComponentAttributes(component) { for(const attribute in component) { if(false === this.validComponentAttributes.includes(attribute)) { const error = new Error(`Invalid component attribute ${attribute}`) error.code = 'E_OAS_INVALID_SPECIFICATION' throw error } } } }