From 78c1c8371346c131acd37378401e1d1dd1030aed Mon Sep 17 00:00:00 2001 From: Gabriel Ratcliff Date: Tue, 21 Jan 2020 16:57:04 -0800 Subject: [PATCH] Log decoration - preliminary work: - Building out 'findOperation' class method on OAS. Enables ingestion of an log, and combines it with the existing oas spec to determine the target path --- package.json | 1 + src/lib/get-user-variable.js | 6 +-- src/oas.js | 83 ++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5bb21c33..e23fae5d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "node-status": "^1.0.0", "oas-normalize": "1.0.0", "open": "^7.0.0", + "path-to-regexp": "^6.1.0", "prompt-sync": "^4.1.4", "request": "^2.88.0", "swagger-inline": "3.0.0", diff --git a/src/lib/get-user-variable.js b/src/lib/get-user-variable.js index ecc70bb5..e1260796 100644 --- a/src/lib/get-user-variable.js +++ b/src/lib/get-user-variable.js @@ -2,7 +2,7 @@ function getKey(user, property) { return user[property] || null; } -function getUserVariable(user, property, selectedApp = false) { +module.exports = function getUserVariable(user, property, selectedApp = false) { if (user.keys) { if (selectedApp) { return getKey( @@ -15,6 +15,4 @@ function getUserVariable(user, property, selectedApp = false) { } return getKey(user, property); -} - -module.exports = getUserVariable; +}; diff --git a/src/oas.js b/src/oas.js index c9b2e908..5bd04326 100644 --- a/src/oas.js +++ b/src/oas.js @@ -1,4 +1,5 @@ /* eslint-disable max-classes-per-file */ +const { pathToRegexp, match } = require('path-to-regexp'); const getPathOperation = require('./lib/get-path-operation'); const getUserVariable = require('./lib/get-user-variable'); @@ -101,6 +102,71 @@ function normalizedUrl(oas) { return ensureProtocol(url); } +function normalizePath(path) { + const curlBacketMatch = /{(.*?)}/; + + return path + .split('/') + .map(p => { + const pathMatch = curlBacketMatch.exec(p); + if (pathMatch) return `:${pathMatch[1]}`; + return p; + }) + .join('/'); +} + +function generatePathMatches(paths, pathName) { + return Object.keys(paths) + .map(path => { + const cleanedPath = normalizePath(path); + const matchStatement = match(cleanedPath, { decode: decodeURIComponent }); + const matchResult = matchStatement(pathName); + + return { + path, + ref: paths[path], + match: matchResult, + params: matchResult && Object.keys(matchResult.params).length ? matchResult.params : {}, + }; + }) + .filter(p => p.match); +} + +function filterPathMethods(pathMatches, targetMethod) { + const regExp = pathToRegexp(targetMethod); + return pathMatches + .map(p => { + const captures = Object.keys(p.ref).filter(r => regExp.exec(r)); + + if (captures.length) { + const method = captures[0]; + return { + path: p.path, + ref: p.ref[method], + params: p.params, + }; + } + return undefined; + }) + .filter(p => p); +} + +function findTargetPath(pathMatches) { + let minCount = Object.keys(pathMatches[0].params).length; + let candidate; + + for (let m = 0; m < pathMatches.length; m += 1) { + const selection = pathMatches[m]; + const paramCount = Object.keys(selection.params).length; + if (paramCount <= minCount) { + minCount = paramCount; + candidate = selection; + } + } + + return candidate; +} + class Oas { constructor(oas, user) { Object.assign(this, oas); @@ -128,6 +194,23 @@ class Oas { const operation = getPathOperation(this, { swagger: { path }, api: { method } }); return new Operation(this, path, method, operation); } + + findOperation(logInput) { + const { url, method } = logInput.request.log.entries[0].request; + const { origin, pathname } = new URL(url); + const { servers, paths } = this; + + const targetServer = servers.find(s => s.url === origin); + if (!targetServer) return undefined; + + const annotatedPaths = generatePathMatches(paths, pathname); + if (!annotatedPaths.length) return undefined; + + const includesMethod = filterPathMethods(annotatedPaths, method); + if (!includesMethod.length) return undefined; + + return findTargetPath(includesMethod); + } } module.exports = Oas;