UNPKG

@stoplight/spectral

Version:

A flexible object linter with out of the box support for OpenAPI v2 and v3.

136 lines (127 loc) 5.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const pathRegex = /(\{[a-zA-Z0-9_-]+\})+/g; exports.oasPathParam = (targetVal, _options, paths, vals) => { const results = []; let object = targetVal; const { resolved } = vals; if (!resolved) { console.warn('oasPathParam expects a resolved object, but none was provided. Results may not be correct.'); } else { object = resolved; } if (!object.paths) { return []; } const uniquePaths = {}; for (const path in object.paths) { if (!object.paths[path]) continue; const normalized = path.replace(pathRegex, '%'); if (uniquePaths[normalized]) { results.push(generateResult(`The paths "**${uniquePaths[normalized]}**" and "**${path}**" are equivalent. To fix, remove one of the paths or merge them together.`, [...paths.given, 'paths'])); } else { uniquePaths[normalized] = path; } const pathElements = {}; while (true) { const match = pathRegex.exec(path); if (match && match.length > 0) { const p = match[0].replace(/[\{\}]/g, ''); if (pathElements[p]) { results.push(generateResult(`The path "**${path}**" uses the parameter "**{${p}}**" multiple times. Path parameters must be unique. To fix, update the path so that all parameter names are unique.`, [...paths.given, 'paths', path])); } else { pathElements[p] = {}; } continue; } break; } const topParams = {}; if (object.paths[path].parameters) { for (const p of object.paths[path].parameters) { if (p.in && p.in === 'path' && p.name) { if (!p.required) { results.push(generateResult(requiredMessage(p.name), [...paths.given, 'paths', path, 'parameters'])); } if (topParams[p.name]) { results.push(generateResult(uniqueDefinitionMessage(p.name), [...paths.given, 'paths', path, 'parameters'])); continue; } topParams[p.name] = [...paths.given, 'paths', path, 'parameters']; } } } const operationParams = {}; for (const op in object.paths[path]) { if (!object.paths[path][op]) continue; if (op === 'parameters') { continue; } const parameters = object.paths[path][op].parameters; if (parameters) { const tmp = {}; for (const i in parameters) { if (!parameters.hasOwnProperty(i)) continue; const p = parameters[i]; if (p.in && p.in === 'path' && p.name) { const parameterPath = ['paths', path, op, 'parameters', i]; if (!p.required) { results.push(generateResult(requiredMessage(p.name), [...paths.given, ...parameterPath])); } if (tmp[p.name]) { results.push(generateResult(uniqueDefinitionMessage(p.name), [...paths.given, ...parameterPath])); continue; } else if (operationParams[p.name]) { continue; } tmp[p.name] = {}; operationParams[p.name] = parameterPath; } } } } for (const p in pathElements) { if (!pathElements[p]) continue; if (!topParams[p] && !operationParams[p]) { results.push(generateResult(`The path "**${path}**" uses a parameter "**{${p}}**" that does not have a corresponding definition. To fix, add a path parameter with the name "**${p}**".`, [...paths.given, 'paths', path])); } } for (const paramObj of [topParams, operationParams]) { for (const p in paramObj) { if (!paramObj[p]) continue; if (!pathElements[p]) { const resPath = paramObj[p]; results.push(generateResult(`Parameter "**${p}**" is not used in the path "**${path}**". Unused parameters are not allowed. To fix, remove this parameter.`, [...paths.given, ...resPath])); } } } } return results; }; function generateResult(message, path) { return { message, path, }; } const requiredMessage = (name) => `Path parameter "**${name}**" must have a \`required\` that is set to \`true\`. To fix, mark this parameter as required.`; const uniqueDefinitionMessage = (name) => `Path parameter '**${name}**' is defined multiple times. Path parameters must be unique. To fix, remove the duplicate parameters.`; //# sourceMappingURL=index.js.map