@loopback/openapi-spec-builder
Version:
Make it easy to create OpenAPI (Swagger) specification documents in your tests using the builder pattern.
352 lines • 10.8 kB
JavaScript
"use strict";
// Copyright IBM Corp. and LoopBack contributors 2017,2020. All Rights Reserved.
// Node module: @loopback/openapi-spec-builder
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComponentsSpecBuilder = exports.OperationSpecBuilder = exports.OpenApiSpecBuilder = exports.BuilderBase = exports.aComponentsSpec = exports.anOperationSpec = exports.anOpenApiSpec = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
/**
* Create a new instance of OpenApiSpecBuilder.
*
* @param basePath - The base path on which the API is served.
*/
function anOpenApiSpec() {
return new OpenApiSpecBuilder();
}
exports.anOpenApiSpec = anOpenApiSpec;
/**
* Create a new instance of OperationSpecBuilder.
*/
function anOperationSpec() {
return new OperationSpecBuilder();
}
exports.anOperationSpec = anOperationSpec;
/**
* Create a new instance of ComponentsSpecBuilder.
*/
function aComponentsSpec() {
return new ComponentsSpecBuilder();
}
exports.aComponentsSpec = aComponentsSpec;
class BuilderBase {
constructor(initialSpec) {
this._spec = initialSpec;
}
/**
* Add a custom (extension) property to the spec object.
*
* @param key - The property name starting with "x-".
* @param value - The property value.
*/
withExtension(key,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value) {
(0, assert_1.default)(key.startsWith('x-'), `Invalid extension ${key}, extension keys must be prefixed with "x-"`);
// `this._spec[key] = value;` is broken in TypeScript 3.5
// See https://github.com/microsoft/TypeScript/issues/31661
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._spec[key] = value;
return this;
}
/**
* Build the spec object.
*/
build() {
// TODO(bajtos): deep-clone
return this._spec;
}
}
exports.BuilderBase = BuilderBase;
/**
* A builder for creating OpenApiSpec documents.
*/
class OpenApiSpecBuilder extends BuilderBase {
/**
* @param basePath - The base path on which the API is served.
*/
constructor() {
super({
openapi: '3.0.0',
info: {
title: 'LoopBack Application',
version: '1.0.0',
},
paths: {},
servers: [{ url: '/' }],
});
}
/**
* Define a new OperationObject at the given path and verb (method).
*
* @param verb - The HTTP verb.
* @param path - The path relative to basePath.
* @param spec - Additional specification of the operation.
*/
withOperation(verb, path, spec) {
if (spec instanceof OperationSpecBuilder)
spec = spec.build();
if (!this._spec.paths[path])
this._spec.paths[path] = {};
this._spec.paths[path][verb] = spec;
return this;
}
/**
* Define a new operation that returns a string response.
*
* @param verb - The HTTP verb.
* @param path - The path relative to basePath.
* @param operationName - The name of the controller method implementing
* this operation (`x-operation-name` field).
*/
withOperationReturningString(verb, path, operationName) {
const spec = anOperationSpec().withStringResponse(200);
if (operationName)
spec.withOperationName(operationName);
return this.withOperation(verb, path, spec);
}
/**
* Define a new ComponentsObject.
*
* @param spec - Specification of the components.
*/
withComponents(spec) {
if (spec instanceof ComponentsSpecBuilder)
spec = spec.build();
if (!this._spec.components)
this._spec.components = spec;
return this;
}
}
exports.OpenApiSpecBuilder = OpenApiSpecBuilder;
/**
* A builder for creating OperationObject specifications.
*/
class OperationSpecBuilder extends BuilderBase {
constructor() {
super({
responses: { '200': { description: 'An undocumented response body.' } },
});
}
/**
* Describe a response for a given HTTP status code.
* @param status - HTTP status code or string "default"
* @param responseSpec - Specification of the response
*/
withResponse(status, responseSpec) {
// OpenAPI spec uses string indices, i.e. 200 OK uses "200" as the index
this._spec.responses[status.toString()] = responseSpec;
return this;
}
withStringResponse(status = 200) {
return this.withResponse(status, {
description: 'The string result.',
content: {
'text/plain': {
schema: { type: 'string' },
},
},
});
}
/**
* Describe one more parameters accepted by the operation.
* Note that parameters are positional in OpenAPI Spec, therefore
* the first call of `withParameter` defines the first parameter,
* the second call defines the second parameter, etc.
* @param parameterSpecs
*/
withParameter(...parameterSpecs) {
if (!this._spec.parameters)
this._spec.parameters = [];
this._spec.parameters.push(...parameterSpecs);
return this;
}
withRequestBody(requestBodySpec) {
this._spec.requestBody = requestBodySpec;
return this;
}
/**
* Define the operation name (controller method name).
*
* @param name - The name of the controller method implementing this operation.
*/
withOperationName(name) {
this.withExtension('x-operation-name', name);
this.setupOperationId();
return this;
}
/**
* Define the controller name (controller name).
*
* @param name - The name of the controller containing this operation.
*/
withControllerName(name) {
this.withExtension('x-controller-name', name);
this.setupOperationId();
return this;
}
/**
* Set up the `operationId` if not configured
*/
setupOperationId() {
if (this._spec.operationId)
return;
const controllerName = this._spec['x-controller-name'];
const operationName = this._spec['x-operation-name'];
if (controllerName && operationName) {
// Build the operationId as `<controllerName>.<operationName>`
// Please note API explorer (https://github.com/swagger-api/swagger-js/)
// will normalize it as `<controllerName>_<operationName>`
this._spec.operationId = controllerName + '.' + operationName;
}
}
/**
* Define the operationId
* @param operationId - Operation id
*/
withOperationId(operationId) {
this._spec.operationId = operationId;
return this;
}
/**
* Describe tags associated with the operation
* @param tags
*/
withTags(tags) {
if (!this._spec.tags)
this._spec.tags = [];
if (typeof tags === 'string')
tags = [tags];
this._spec.tags.push(...tags);
return this;
}
}
exports.OperationSpecBuilder = OperationSpecBuilder;
/**
* A builder for creating ComponentsObject specifications.
*/
class ComponentsSpecBuilder extends BuilderBase {
constructor() {
super({});
}
/**
* Define a component schema.
*
* @param name - The name of the schema
* @param schema - Specification of the schema
*
*/
withSchema(name, schema) {
if (!this._spec.schemas)
this._spec.schemas = {};
this._spec.schemas[name] = schema;
return this;
}
/**
* Define a component response.
*
* @param name - The name of the response
* @param response - Specification of the response
*
*/
withResponse(name, response) {
if (!this._spec.responses)
this._spec.responses = {};
this._spec.responses[name] = response;
return this;
}
/**
* Define a component parameter.
*
* @param name - The name of the parameter
* @param parameter - Specification of the parameter
*
*/
withParameter(name, parameter) {
if (!this._spec.parameters)
this._spec.parameters = {};
this._spec.parameters[name] = parameter;
return this;
}
/**
* Define a component example.
*
* @param name - The name of the example
* @param example - Specification of the example
*
*/
withExample(name, example) {
if (!this._spec.examples)
this._spec.examples = {};
this._spec.examples[name] = example;
return this;
}
/**
* Define a component request body.
*
* @param name - The name of the request body
* @param requestBody - Specification of the request body
*
*/
withRequestBody(name, requestBody) {
if (!this._spec.requestBodies)
this._spec.requestBodies = {};
this._spec.requestBodies[name] = requestBody;
return this;
}
/**
* Define a component header.
*
* @param name - The name of the header
* @param header - Specification of the header
*
*/
withHeader(name, header) {
if (!this._spec.headers)
this._spec.headers = {};
this._spec.headers[name] = header;
return this;
}
/**
* Define a component security scheme.
*
* @param name - The name of the security scheme
* @param securityScheme - Specification of the security scheme
*
*/
withSecurityScheme(name, securityScheme) {
if (!this._spec.securitySchemes)
this._spec.securitySchemes = {};
this._spec.securitySchemes[name] = securityScheme;
return this;
}
/**
* Define a component link.
*
* @param name - The name of the link
* @param link - Specification of the link
*
*/
withLink(name, link) {
if (!this._spec.links)
this._spec.links = {};
this._spec.links[name] = link;
return this;
}
/**
* Define a component callback.
*
* @param name - The name of the callback
* @param callback - Specification of the callback
*
*/
withCallback(name, callback) {
if (!this._spec.callbacks)
this._spec.callbacks = {};
this._spec.callbacks[name] = callback;
return this;
}
}
exports.ComponentsSpecBuilder = ComponentsSpecBuilder;
//# sourceMappingURL=openapi-spec-builder.js.map