UNPKG

@ardatan/openapi-to-graphql

Version:

Generates a GraphQL schema for a given OpenAPI Specification (OAS)

992 lines 35.2 kB
"use strict"; // Copyright IBM Corp. 2018. All Rights Reserved. // Node module: openapi-to-graphql // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); // Imports: const swagger2openapi_1 = require("swagger2openapi"); const oas_validator_1 = require("oas-validator"); const debug_1 = require("debug"); const utils_1 = require("./utils"); const pluralize = require("pluralize"); const httpLog = debug_1.default('http'); const preprocessingLog = debug_1.default('preprocessing'); const translationLog = debug_1.default('translation'); // OAS constants exports.OAS_OPERATIONS = [ 'get', 'put', 'post', 'patch', 'delete', 'options', 'head' ]; exports.SUCCESS_STATUS_RX = /2[0-9]{2}|2XX/; /** * Resolves on a validated OAS 3 for the given spec (OAS 2 or OAS 3), or rejects * if errors occur. */ function getValidOAS3(spec, options) { return __awaiter(this, void 0, void 0, function* () { // CASE: translate if (typeof spec.swagger === 'string' && spec.swagger === '2.0') { preprocessingLog(`Received OpenAPI Specification 2.0 - going to translate...`); const result = yield swagger2openapi_1.convertObj(spec, {}); return result.openapi; // CASE: validate } else if (typeof spec.openapi === 'string' && /^3/.test(spec.openapi)) { if (!options.skipSchemaValidation) { preprocessingLog(`Received OpenAPI Specification 3.0.x - going to validate...`); const valid = yield oas_validator_1.validate(spec, {}); if (!valid) { throw new Error(`Validation of OpenAPI Specification failed.`); } preprocessingLog(`OpenAPI Specification is validated`); } return spec; } else { throw new Error(`Invalid specification provided`); } }); } exports.getValidOAS3 = getValidOAS3; /** * Counts the number of operations in an OAS. */ function countOperations(oas) { let numOps = 0; for (let path in oas.paths) { for (let method in oas.paths[path]) { if (isOperation(method)) { numOps++; } } } return numOps; } exports.countOperations = countOperations; /** * Counts the number of operations that translate to queries in an OAS. */ function countOperationsQuery(oas) { let numOps = 0; for (let path in oas.paths) { for (let method in oas.paths[path]) { if (isOperation(method) && method.toLowerCase() === 'get') { numOps++; } } } return numOps; } exports.countOperationsQuery = countOperationsQuery; /** * Counts the number of operations that translate to mutations in an OAS. */ function countOperationsMutation(oas) { let numOps = 0; for (let path in oas.paths) { for (let method in oas.paths[path]) { if (isOperation(method) && method.toLowerCase() !== 'get') { numOps++; } } } return numOps; } exports.countOperationsMutation = countOperationsMutation; /** * Counts the number of operations with a payload definition in an OAS. */ function countOperationsWithPayload(oas) { let numOps = 0; for (let path in oas.paths) { for (let method in oas.paths[path]) { if (isOperation(method) && typeof oas.paths[path][method].requestBody === 'object') { numOps++; } } } return numOps; } exports.countOperationsWithPayload = countOperationsWithPayload; /** * Resolves the given reference in the given object. */ function resolveRef(ref, oas) { // Break path into individual tokens const parts = ref.split('/'); const resolvedObject = resolveRefHelper(oas, parts); if (resolvedObject !== null) { return resolvedObject; } else { throw new Error(`Could not resolve reference '${ref}'`); } } exports.resolveRef = resolveRef; /** * Helper for resolveRef * * @param parts The path to be resolved, but broken into tokens */ function resolveRefHelper(obj, parts) { if (parts.length === 0) { return obj; } const firstElement = parts.splice(0, 1)[0]; if (firstElement in obj) { return resolveRefHelper(obj[firstElement], parts); } else if (firstElement === '#') { return resolveRefHelper(obj, parts); } else { return null; } } /** * Returns the base URL to use for the given operation. */ function getBaseUrl(operation) { // Check for servers: if (!Array.isArray(operation.servers) || operation.servers.length === 0) { throw new Error(`No servers defined for operation '${operation.operationString}'`); } // Check for local servers if (Array.isArray(operation.servers) && operation.servers.length > 0) { const url = buildUrl(operation.servers[0]); if (Array.isArray(operation.servers) && operation.servers.length > 1) { httpLog(`Warning: Randomly selected first server '${url}'`); } return url.replace(/\/$/, ''); } const oas = operation.oas; if (Array.isArray(oas.servers) && oas.servers.length > 0) { const url = buildUrl(oas.servers[0]); if (Array.isArray(oas.servers) && oas.servers.length > 1) { httpLog(`Warning: Randomly selected first server '${url}'`); } return url.replace(/\/$/, ''); } throw new Error('Cannot find a server to call'); } exports.getBaseUrl = getBaseUrl; /** * Returns the default URL for a given OAS server object. */ function buildUrl(server) { let url = server.url; // Replace with variable defaults, if applicable if (typeof server.variables === 'object' && Object.keys(server.variables).length > 0) { for (let variableKey in server.variables) { // TODO: check for default? Would be invalid OAS url = url.replace(`{${variableKey}}`, server.variables[variableKey].default.toString()); } } return url; } /** * Returns object/array/scalar where all object keys (if applicable) are * sanitized. */ function sanitizeObjectKeys(obj, // obj does not necessarily need to be an object caseStyle = CaseStyle.camelCase) { const cleanKeys = (obj) => { // Case: no (response) data if (obj === null || typeof obj === 'undefined') { return null; // Case: array } else if (Array.isArray(obj)) { return obj.map(cleanKeys); // Case: object } else if (typeof obj === 'object') { const res = {}; for (const key in obj) { const saneKey = sanitize(key, caseStyle); if (Object.prototype.hasOwnProperty.call(obj, key)) { res[saneKey] = cleanKeys(obj[key]); } } return res; // Case: scalar } else { return obj; } }; return cleanKeys(obj); } exports.sanitizeObjectKeys = sanitizeObjectKeys; /** * Desanitizes keys in given object by replacing them with the keys stored in * the given mapping. */ function desanitizeObjectKeys(obj, mapping = {}) { const replaceKeys = obj => { if (obj === null) { return null; } else if (Array.isArray(obj)) { return obj.map(replaceKeys); } else if (typeof obj === 'object') { const res = {}; for (let key in obj) { if (key in mapping) { const rawKey = mapping[key]; if (Object.prototype.hasOwnProperty.call(obj, key)) { res[rawKey] = replaceKeys(obj[key]); } } else { res[key] = replaceKeys(obj[key]); } } return res; } else { return obj; } }; return replaceKeys(obj); } exports.desanitizeObjectKeys = desanitizeObjectKeys; /** * Replaces the path parameter in the given path with values in the given args. * Furthermore adds the query parameters for a request. */ function instantiatePathAndGetQuery(path, parameters, args, // NOTE: argument keys are sanitized! data) { const query = {}; const headers = {}; // Case: nothing to do if (Array.isArray(parameters)) { // Iterate parameters: for (const param of parameters) { const sanitizedParamName = sanitize(param.name, !data.options.simpleNames ? CaseStyle.camelCase : CaseStyle.simple); if (sanitizedParamName && sanitizedParamName in args) { switch (param.in) { // Path parameters case 'path': path = path.replace(`{${param.name}}`, args[sanitizedParamName]); break; // Query parameters case 'query': query[param.name] = args[sanitizedParamName]; break; // Header parameters case 'header': headers[param.name] = args[sanitizedParamName]; break; // Cookie parameters case 'cookie': if (!('cookie' in headers)) { headers['cookie'] = ''; } headers['cookie'] += `${param.name}=${args[sanitizedParamName]}; `; break; default: httpLog(`Warning: The parameter location '${param.in}' in the ` + `parameter '${param.name}' of operation '${path}' is not ` + `supported`); } } else { httpLog(`Warning: The parameter '${param.name}' of operation '${path}' ` + `could not be found`); } } } return { path, query, headers }; } exports.instantiatePathAndGetQuery = instantiatePathAndGetQuery; /** * Returns the GraphQL type that the provided schema should be made into * * Does not consider allOf, anyOf, oneOf, or not (handled separately) */ function getSchemaTargetGraphQLType(schema, data) { // CASE: object if (schema.type === 'object' || typeof schema.properties === 'object') { // TODO: additionalProperties is more like a flag than a type itself // CASE: arbitrary JSON if (typeof schema.additionalProperties === 'object') { return 'json'; } else { return 'object'; } } // CASE: array if (schema.type === 'array' || 'items' in schema) { return 'list'; } // CASE: enum if (Array.isArray(schema.enum)) { return 'enum'; } // CASE: a type is present if (typeof schema.type === 'string') { // Special edge cases involving the schema format if (typeof schema.format === 'string') { /** * CASE: 64 bit int - return number instead of integer, leading to use of * GraphQLFloat, which can support 64 bits: */ if (schema.type === 'integer' && schema.format === 'int64') { return 'number'; // CASE: id } else if (schema.type === 'string' && (schema.format === 'uuid' || // Custom ID format (Array.isArray(data.options.idFormats) && data.options.idFormats.includes(schema.format)))) { return 'id'; } } return schema.type; } return null; } exports.getSchemaTargetGraphQLType = getSchemaTargetGraphQLType; /** * Identifies common path components in the given list of paths. Returns these * components as well as an updated list of paths where the common prefix was * removed. */ function extractBasePath(paths) { if (paths.length <= 1) { return { basePath: '/', updatedPaths: paths }; } let basePathComponents = paths[0].split('/'); for (let path of paths) { if (basePathComponents.length === 0) { break; } const pathComponents = path.split('/'); for (let i = 0; i < pathComponents.length; i++) { if (i < basePathComponents.length) { if (pathComponents[i] !== basePathComponents[i]) { basePathComponents = basePathComponents.slice(0, i); } } else { break; } } } const updatedPaths = paths.map(path => path .split('/') .slice(basePathComponents.length) .join('/')); let basePath = basePathComponents.length === 0 || (basePathComponents.length === 1 && basePathComponents[0] === '') ? '/' : basePathComponents.join('/'); return { basePath, updatedPaths }; } function isIdParam(part) { return /^{.*(id|name|key).*}$/gi.test(part); } function isSingularParam(part, nextPart) { return `\{${pluralize.singular(part)}\}` === nextPart; } /** * Infers a resource name from the given URL path. * * For example, turns "/users/{userId}/car" into "userCar". */ function inferResourceNameFromPath(path) { const parts = path.split('/'); let pathNoPathParams = parts.reduce((path, part, i) => { if (!/{/g.test(part)) { if (parts[i + 1] && (isIdParam(parts[i + 1]) || isSingularParam(part, parts[i + 1]))) { return path + capitalize(pluralize.singular(part)); } else { return path + capitalize(part); } } else { return path; } }, ''); return pathNoPathParams; } exports.inferResourceNameFromPath = inferResourceNameFromPath; /** * Returns JSON-compatible schema required by the given endpoint - or null if it * does not exist. */ function getRequestBodyObject(endpoint, oas) { if (typeof endpoint.requestBody === 'object') { let requestBodyObject = endpoint.requestBody; // Make sure we have a RequestBodyObject: if (typeof requestBodyObject.$ref === 'string') { requestBodyObject = resolveRef(requestBodyObject.$ref, oas); } else { requestBodyObject = requestBodyObject; } if (typeof requestBodyObject.content === 'object') { const content = requestBodyObject.content; // Prioritize content-type JSON if (Object.keys(content).includes('application/json')) { return { payloadContentType: 'application/json', requestBodyObject }; } else if (Object.keys(content).includes('application/x-www-form-urlencoded')) { return { payloadContentType: 'application/x-www-form-urlencoded', requestBodyObject }; } else { // Pick first (random) content type const randomContentType = Object.keys(content)[0]; return { payloadContentType: randomContentType, requestBodyObject }; } } } return { payloadContentType: null, requestBodyObject: null }; } exports.getRequestBodyObject = getRequestBodyObject; /** * Returns the request schema (if any) for an endpoint at given path and method, * a dictionary of names from different sources (if available), and whether the * request schema is required for the endpoint. */ function getRequestSchemaAndNames(path, method, oas) { const endpoint = oas.paths[path][method]; const { payloadContentType, requestBodyObject } = getRequestBodyObject(endpoint, oas); if (payloadContentType) { let payloadSchema = requestBodyObject.content[payloadContentType].schema; // Get resource name from different sources let fromRef; if ('$ref' in payloadSchema) { fromRef = payloadSchema['$ref'].split('/').pop(); payloadSchema = resolveRef(payloadSchema['$ref'], oas); } let payloadSchemaNames = { fromRef, fromSchema: payloadSchema.title, fromPath: inferResourceNameFromPath(path) }; // Determine if request body is required: const payloadRequired = typeof requestBodyObject.required === 'boolean' ? requestBodyObject.required : false; /** * Edge case: if request body content-type is not application/json or * application/x-www-form-urlencoded, do not parse it. * * Instead, treat the request body as a black box and send it as a string * with the proper content-type header */ if (payloadContentType !== 'application/json' && payloadContentType !== 'application/x-www-form-urlencoded') { const saneContentTypeName = uncapitalize(payloadContentType.split('/').reduce((name, term) => { return name + capitalize(term); })); payloadSchemaNames = { fromPath: saneContentTypeName }; let description = `String represents payload of content type '${payloadContentType}'`; if ('description' in payloadSchema && typeof payloadSchema['description'] === 'string') { description += `\n\nOriginal top level description: '${payloadSchema['description']}'`; } payloadSchema = { description: description, type: 'string' }; } return { payloadContentType, payloadSchema, payloadSchemaNames, payloadRequired }; } return { payloadRequired: false }; } exports.getRequestSchemaAndNames = getRequestSchemaAndNames; /** * Returns JSON-compatible schema produced by the given endpoint - or null if it * does not exist. */ function getResponseObject(endpoint, statusCode, oas) { if (typeof endpoint.responses === 'object') { const responses = endpoint.responses; if (typeof responses[statusCode] === 'object') { let responseObject = responses[statusCode]; // Make sure we have a ResponseObject: if (typeof responseObject.$ref === 'string') { responseObject = resolveRef(responseObject.$ref, oas); } else { responseObject = responseObject; } if (responseObject.content && typeof responseObject.content !== 'undefined') { const content = responseObject.content; // Prioritize content-type JSON if (Object.keys(content).includes('application/json')) { return { responseContentType: 'application/json', responseObject }; } else { // Pick first (random) content type const randomContentType = Object.keys(content)[0]; return { responseContentType: randomContentType, responseObject }; } } } } return { responseContentType: null, responseObject: null }; } exports.getResponseObject = getResponseObject; /** * Returns the response schema for endpoint at given path and method and with * the given status code, and a dictionary of names from different sources (if * available). */ function getResponseSchemaAndNames(path, method, oas, data, options) { const endpoint = oas.paths[path][method]; const statusCode = getResponseStatusCode(path, method, oas, data); if (!statusCode) { return {}; } let { responseContentType, responseObject } = getResponseObject(endpoint, statusCode, oas); if (responseContentType) { let responseSchema = responseObject.content[responseContentType].schema; let fromRef; if ('$ref' in responseSchema) { fromRef = responseSchema['$ref'].split('/').pop(); responseSchema = resolveRef(responseSchema['$ref'], oas); } const responseSchemaNames = { fromRef, fromSchema: responseSchema.title, fromPath: inferResourceNameFromPath(path) }; /** * Edge case: if response body content-type is not application/json, do not * parse. */ if (responseContentType !== 'application/json') { let description = 'Placeholder to access non-application/json response bodies'; if ('description' in responseSchema && typeof responseSchema['description'] === 'string') { description += `\n\nOriginal top level description: '${responseSchema['description']}'`; } responseSchema = { description: description, type: 'string' }; } return { responseContentType, responseSchema, responseSchemaNames, statusCode }; } else { /** * GraphQL requires that objects must have some properties. * * To allow some operations (such as those with a 204 HTTP code) to be * included in the GraphQL interface, we added the fillEmptyResponses * option, which will simply create a placeholder to allow access. */ if (options.fillEmptyResponses) { return { responseSchemaNames: { fromPath: inferResourceNameFromPath(path) }, responseContentType: 'application/json', responseSchema: { description: 'Placeholder to support operations with no response schema', type: 'string' } }; } return {}; } } exports.getResponseSchemaAndNames = getResponseSchemaAndNames; /** * Returns the success status code for the operation at the given path and * method (or null). */ function getResponseStatusCode(path, method, oas, data) { const endpoint = oas.paths[path][method]; if (typeof endpoint.responses === 'object') { const codes = Object.keys(endpoint.responses); const successCodes = codes.filter(code => { return exports.SUCCESS_STATUS_RX.test(code); }); if (successCodes.length === 1) { return successCodes[0]; } else if (successCodes.length > 1) { utils_1.handleWarning({ typeKey: 'MULTIPLE_RESPONSES', message: `Operation '${formatOperationString(method, path, oas.info.title)}' ` + `contains multiple possible successful response object ` + `(HTTP code 200-299 or 2XX). Only one can be chosen.`, mitigationAddendum: `The response object with the HTTP code ` + `${successCodes[0]} will be selected`, data, log: translationLog }); return successCodes[0]; } } return null; } exports.getResponseStatusCode = getResponseStatusCode; /** * Returns an hash containing the links defined in the given endpoint. */ function getEndpointLinks(path, method, oas, data) { const links = {}; const endpoint = oas.paths[path][method]; const statusCode = getResponseStatusCode(path, method, oas, data); if (!statusCode) { return links; } if (typeof endpoint.responses === 'object') { const responses = endpoint.responses; if (typeof responses[statusCode] === 'object') { let response = responses[statusCode]; if (typeof response.$ref === 'string') { response = resolveRef(response.$ref, oas); } // Here, we can be certain we have a ResponseObject: response = response; if (typeof response.links === 'object') { const epLinks = response.links; for (let linkKey in epLinks) { let link = epLinks[linkKey]; // Make sure we have LinkObjects: if (typeof link.$ref === 'string') { link = resolveRef(link['$ref'], oas); } else { link = link; } links[linkKey] = link; } } } } return links; } exports.getEndpointLinks = getEndpointLinks; /** * Returns the list of parameters for the endpoint at the given method and path. * Resolves possible references. */ function getParameters(path, method, oas) { let parameters = []; if (!isOperation(method)) { translationLog(`Warning: attempted to get parameters for ${method} ${path}, ` + `which is not an operation.`); return parameters; } const pathItemObject = oas.paths[path]; const pathParams = pathItemObject.parameters; // First, consider parameters in Path Item Object: if (Array.isArray(pathParams)) { const pathItemParameters = pathParams.map(p => { if (typeof p.$ref === 'string') { // Here we know we have a parameter object: return resolveRef(p['$ref'], oas); } else { // Here we know we have a parameter object: return p; } }); parameters = parameters.concat(pathItemParameters); } // Second, consider parameters in Operation Object: const opObject = oas.paths[path][method]; const opObjectParameters = opObject.parameters; if (Array.isArray(opObjectParameters)) { const opParameters = opObjectParameters.map(p => { if (typeof p.$ref === 'string') { // Here we know we have a parameter object: return resolveRef(p['$ref'], oas); } else { // Here we know we have a parameter object: return p; } }); parameters = parameters.concat(opParameters); } return parameters; } exports.getParameters = getParameters; /** * Returns an array of server objects for the opeartion at the given path and * method. Considers in the following order: global server definitions, * definitions at the path item, definitions at the operation, or the OAS * default. */ function getServers(path, method, oas) { let servers = []; // Global server definitions: if (Array.isArray(oas.servers) && oas.servers.length > 0) { servers = oas.servers; } // Path item server definitions override global: const pathItem = oas.paths[path]; if (Array.isArray(pathItem.servers) && pathItem.servers.length > 0) { servers = pathItem.servers; } // Operation server definitions override path item: const operationObj = pathItem[method]; if (Array.isArray(operationObj.servers) && operationObj.servers.length > 0) { servers = operationObj.servers; } // Default, in case there is no server: if (servers.length === 0) { let server = { url: '/' // TODO: avoid double-slashes }; servers.push(server); } return servers; } exports.getServers = getServers; /** * Returns a map of Security Scheme definitions, identified by keys. Resolves * possible references. */ function getSecuritySchemes(oas) { // Collect all security schemes: const securitySchemes = {}; if (typeof oas.components === 'object' && typeof oas.components.securitySchemes === 'object') { for (let schemeKey in oas.components.securitySchemes) { const obj = oas.components.securitySchemes[schemeKey]; // Ensure we have actual SecuritySchemeObject: if (typeof obj.$ref === 'string') { // Result of resolution will be SecuritySchemeObject: securitySchemes[schemeKey] = resolveRef(obj.$ref, oas); } else { // We already have a SecuritySchemeObject: securitySchemes[schemeKey] = obj; } } } return securitySchemes; } exports.getSecuritySchemes = getSecuritySchemes; /** * Returns the list of sanitized keys of non-OAuth2 security schemes * required by the operation at the given path and method. */ function getSecurityRequirements(path, method, securitySchemes, oas) { const results = []; // First, consider global requirements: const globalSecurity = oas.security; if (globalSecurity && typeof globalSecurity !== 'undefined') { for (let secReq of globalSecurity) { for (let schemaKey in secReq) { if (securitySchemes[schemaKey] && typeof securitySchemes[schemaKey] === 'object' && securitySchemes[schemaKey].def.type !== 'oauth2') { results.push(schemaKey); } } } } // Local: const operation = oas.paths[path][method]; const localSecurity = operation.security; if (localSecurity && typeof localSecurity !== 'undefined') { for (let secReq of localSecurity) { for (let schemaKey in secReq) { if (securitySchemes[schemaKey] && typeof securitySchemes[schemaKey] === 'object' && securitySchemes[schemaKey].def.type !== 'oauth2') { if (!results.includes(schemaKey)) { results.push(schemaKey); } } } } } return results; } exports.getSecurityRequirements = getSecurityRequirements; var CaseStyle; (function (CaseStyle) { CaseStyle[CaseStyle["simple"] = 0] = "simple"; CaseStyle[CaseStyle["PascalCase"] = 1] = "PascalCase"; CaseStyle[CaseStyle["camelCase"] = 2] = "camelCase"; CaseStyle[CaseStyle["ALL_CAPS"] = 3] = "ALL_CAPS"; // Used for enum values })(CaseStyle = exports.CaseStyle || (exports.CaseStyle = {})); /** * First sanitizes given string and then also camel-cases it. */ function sanitize(str, caseStyle) { /** * Used in conjunction to simpleNames, which only removes illegal * characters and preserves casing */ if (caseStyle === CaseStyle.simple) { return str.replace(/[^a-zA-Z0-9_]/gi, ''); } /** * Remove all GraphQL unsafe characters */ const regex = caseStyle === CaseStyle.ALL_CAPS ? /[^a-zA-Z0-9_]/g // ALL_CAPS has underscores : /[^a-zA-Z0-9]/g; let sanitized = str.split(regex).reduce((path, part) => { if (caseStyle === CaseStyle.ALL_CAPS) { return path + '_' + part; } else { return path + capitalize(part); } }); switch (caseStyle) { case CaseStyle.PascalCase: // The first character in PascalCase should be uppercase sanitized = capitalize(sanitized); break; case CaseStyle.camelCase: // The first character in camelCase should be lowercase sanitized = uncapitalize(sanitized); break; case CaseStyle.ALL_CAPS: sanitized = sanitized.toUpperCase(); break; } // Special case: we cannot start with number, and cannot be empty: if (/^[0-9]/.test(sanitized) || sanitized === '') { sanitized = '_' + sanitized; } return sanitized; } exports.sanitize = sanitize; /** * Sanitizes the given string and stores the sanitized-to-original mapping in * the given mapping. */ function storeSaneName(saneStr, str, mapping) { if (saneStr in mapping && str !== mapping[saneStr]) { // TODO: Follow warning model translationLog(`Warning: '${str}' and '${mapping[saneStr]}' both sanitize ` + `to '${saneStr}' - collision possible. Desanitize to '${str}'.`); } mapping[saneStr] = str; return saneStr; } exports.storeSaneName = storeSaneName; /** * Stringifies and possibly trims the given string to the provided length. */ function trim(str, length) { if (typeof str !== 'string') { str = JSON.stringify(str); } if (str && str.length > length) { str = `${str.substring(0, length)}...`; } return str; } exports.trim = trim; /** * Determines if the given "method" is indeed an operation. Alternatively, the * method could point to other types of information (e.g., parameters, servers). */ function isOperation(method) { return exports.OAS_OPERATIONS.includes(method.toLowerCase()); } exports.isOperation = isOperation; /** * Formats a string that describes an operation in the form: * {name of OAS} {HTTP method in ALL_CAPS} {operation path} * * Also used in preprocessing.ts where Operation objects are being constructed */ function formatOperationString(method, path, title) { if (title) { return `${title} ${method.toUpperCase()} ${path}`; } else { return `${method.toUpperCase()} ${path}`; } } exports.formatOperationString = formatOperationString; /** * Capitalizes a given string */ function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } exports.capitalize = capitalize; /** * Uncapitalizes a given string */ function uncapitalize(str) { return str.charAt(0).toLowerCase() + str.slice(1); } exports.uncapitalize = uncapitalize; /** * For operations that do not have an operationId, generate one */ function generateOperationId(method, path) { return sanitize(`${method} ${path}`, CaseStyle.camelCase); } exports.generateOperationId = generateOperationId; //# sourceMappingURL=oas_3_tools.js.map