From 2e82de2da84e56dd0cdcf67158e99641b48d83ec Mon Sep 17 00:00:00 2001 From: mathis-m Date: Wed, 20 Jan 2021 16:43:16 +0100 Subject: [PATCH] refactor: matcher helpers --- dev-helpers/index.html | 3 + .../fn/helper/extract-schemas.js | 40 +++++++ .../advanced-filter/fn/helper/extract-tags.js | 4 + .../fn/helper/schema-path-base.js | 10 ++ src/core/plugins/advanced-filter/fn/index.js | 12 ++- .../fn/matchers/definition-matcher.js | 20 +--- .../fn/matchers/operation-matcher.js | 102 ++++-------------- .../fn/matchers/tag-matcher.js | 93 ++-------------- .../advanced-filter/fn/regular-filter-expr.js | 14 +++ 9 files changed, 116 insertions(+), 182 deletions(-) create mode 100644 src/core/plugins/advanced-filter/fn/helper/extract-schemas.js create mode 100644 src/core/plugins/advanced-filter/fn/helper/extract-tags.js create mode 100644 src/core/plugins/advanced-filter/fn/helper/schema-path-base.js create mode 100644 src/core/plugins/advanced-filter/fn/regular-filter-expr.js diff --git a/dev-helpers/index.html b/dev-helpers/index.html index 1568962db124..f4d5e0f921f2 100644 --- a/dev-helpers/index.html +++ b/dev-helpers/index.html @@ -42,6 +42,9 @@ window["SwaggerUIStandalonePreset"] = window["swagger-ui-standalone-preset"] // Build a system const ui = SwaggerUIBundle({ + advancedFilter: { + enabled: true + }, url: "https://petstore.swagger.io/v2/swagger.json", dom_id: '#swagger-ui', presets: [ diff --git a/src/core/plugins/advanced-filter/fn/helper/extract-schemas.js b/src/core/plugins/advanced-filter/fn/helper/extract-schemas.js new file mode 100644 index 000000000000..1eea6b03ae39 --- /dev/null +++ b/src/core/plugins/advanced-filter/fn/helper/extract-schemas.js @@ -0,0 +1,40 @@ +/* eslint-disable no-useless-escape */ + +import { List, Map, Set } from "immutable" + +const getNameMatcher = ({ fn, specSelectors }) => { + const { schemaPathBaseRegex } = fn.schemaPathBase(specSelectors) + return new RegExp(`(?<=${schemaPathBaseRegex}).*?(?=(?=\/)|$)`) +} +export const extractSchema = (schemaContainer, system) => { + const nameMatcher = getNameMatcher(system) + return schemaContainer + .map(v => v.getIn(["schema", "$ref"], v.getIn(["schema", "items", "$ref"]))) + .filter(ref => ref != null) + .valueSeq() + .toSet() + .map(requestBodyRef => { + const nameMatch = nameMatcher.exec(requestBodyRef) + return nameMatch ? nameMatch[0] : nameMatch + }) + .filter(name => name != null) +} +export const extractSchemasFromOperations = (operations, system) => { + const { fn, specSelectors } = system + const isOAS3 = specSelectors.isOAS3() + let schemaNamesForOperations = Set() + operations.forEach((path) => { + path.forEach(op => { + schemaNamesForOperations = schemaNamesForOperations + .union(fn.extractSchema(op.getIn(["requestBody", "content"], Map()), system)) + .union(fn.extractSchema(op + .getIn(["parameters"], List()) + .filter(param => param.get("in") === "body"), system)) + .union(fn.extractSchema(op + .getIn(["responses"], Map()) + .map(v => isOAS3 ? v.get("content") : v) + .filter(content => content != null), system)) + }) + }) + return schemaNamesForOperations +} diff --git a/src/core/plugins/advanced-filter/fn/helper/extract-tags.js b/src/core/plugins/advanced-filter/fn/helper/extract-tags.js new file mode 100644 index 000000000000..28806c1c2a2b --- /dev/null +++ b/src/core/plugins/advanced-filter/fn/helper/extract-tags.js @@ -0,0 +1,4 @@ +export const extractTagsFromOperations = (operations) => { + return operations + .flatMap(method => method.map(op => op.get("tags"))) +} diff --git a/src/core/plugins/advanced-filter/fn/helper/schema-path-base.js b/src/core/plugins/advanced-filter/fn/helper/schema-path-base.js new file mode 100644 index 000000000000..42e41787b4af --- /dev/null +++ b/src/core/plugins/advanced-filter/fn/helper/schema-path-base.js @@ -0,0 +1,10 @@ +export const schemaPathBase = (specSelectors) => { + const isOAS3 = specSelectors.isOAS3() + const schemaPathBaseRegex = isOAS3 + ? "\\/components\\/schemas\\/" + : "\\/definitions\\/" + const schemaPathBase = isOAS3 + ? ["components", "schemas"] + : ["definitions"] + return { schemaPathBaseRegex, schemaPathBase } +} diff --git a/src/core/plugins/advanced-filter/fn/index.js b/src/core/plugins/advanced-filter/fn/index.js index 7bcd25f560cd..e5fcfdcaf8be 100644 --- a/src/core/plugins/advanced-filter/fn/index.js +++ b/src/core/plugins/advanced-filter/fn/index.js @@ -1,12 +1,22 @@ import { applyMatchersToSpec } from "./apply-matchers" /* eslint-disable camelcase */ -import { advancedFilterMatcher_operations } from "./matchers/operation-matcher" +import { advancedFilterMatcher_operations, getMatchedOperationsSpec } from "./matchers/operation-matcher" import { advancedFilterMatcher_tags } from "./matchers/tag-matcher" import { advancedFilterMatcher_definitions } from "./matchers/definition-matcher" +import { getRegularFilterExpr } from "./regular-filter-expr" +import { schemaPathBase } from "./helper/schema-path-base" +import { extractSchema, extractSchemasFromOperations } from "./helper/extract-schemas" +import { extractTagsFromOperations } from "./helper/extract-tags" export default { advancedFilterMatcher_operations, advancedFilterMatcher_tags, advancedFilterMatcher_definitions, applyMatchersToSpec, + getRegularFilterExpr, + schemaPathBase, + extractSchema, + extractSchemasFromOperations, + extractTagsFromOperations, + getMatchedOperationsSpec } diff --git a/src/core/plugins/advanced-filter/fn/matchers/definition-matcher.js b/src/core/plugins/advanced-filter/fn/matchers/definition-matcher.js index f6abb6cd17b7..7b6f9449f222 100644 --- a/src/core/plugins/advanced-filter/fn/matchers/definition-matcher.js +++ b/src/core/plugins/advanced-filter/fn/matchers/definition-matcher.js @@ -5,27 +5,15 @@ const escapeRegExp = (string) => { } /* eslint-disable camelcase */ -export const advancedFilterMatcher_definitions = (spec, options, phrase, { specSelectors }) => { +export const advancedFilterMatcher_definitions = (spec, options, phrase, { fn, specSelectors }) => { const isOAS3 = specSelectors.isOAS3() - const schemaPathBase = isOAS3 - ? ["components", "schemas"] - : ["definitions"] + const { schemaPathBase } = fn.schemaPathBase(specSelectors) + const partialSpecResult = fromJS(isOAS3 ? { components: { schemas: {} } } : { definitions: {} }) - phrase = escapeRegExp(phrase) - let expr - try { - expr = new RegExp( - options.get("matchWholeWord") - ? `\\b${phrase}\\b` - : phrase, - !options.get("matchCase") ? "i" : "", - ) - } catch { - // TODO: add errors to state - } + const expr = fn.getRegularFilterExpr(options, escapeRegExp(phrase)) if (expr) { const filteredSchemas = spec .getIn(schemaPathBase) diff --git a/src/core/plugins/advanced-filter/fn/matchers/operation-matcher.js b/src/core/plugins/advanced-filter/fn/matchers/operation-matcher.js index 1da5ee3c6e50..6c7fb0ce8d4f 100644 --- a/src/core/plugins/advanced-filter/fn/matchers/operation-matcher.js +++ b/src/core/plugins/advanced-filter/fn/matchers/operation-matcher.js @@ -1,93 +1,35 @@ -import { fromJS, List, Map, Set } from "immutable" +import { fromJS, Map } from "immutable" // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping const escapeRegExp = (string) => { return string.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&") // $& means the whole matched string } /* eslint-disable camelcase */ -/* eslint-disable no-useless-escape */ - -export const advancedFilterMatcher_operations = (spec, options, phrase, { specSelectors }) => { +export const getMatchedOperationsSpec = (operationFilterPredicate, spec, options, phrase, { fn, specSelectors, getSystem }) => { + const system = getSystem() const isOAS3 = specSelectors.isOAS3() - const schemaPathBaseRegex = isOAS3 - ? "\\/components\\/schemas\\/" - : "\\/definitions\\/" - const schemaPathBase = isOAS3 - ? ["components", "schemas"] - : ["definitions"] + const { schemaPathBase } = fn.schemaPathBase(specSelectors) const partialSpecResult = fromJS(isOAS3 ? { paths: {}, tags: [], components: { schemas: {} } } : { paths: {}, tags: [], definitions: {} }) - const nameMatcher = new RegExp(`(?<=${schemaPathBaseRegex}).*?(?=(?=\/)|$)`) - phrase = escapeRegExp(phrase) - let expr - try { - expr = new RegExp( - options.get("matchWholeWord") - ? `\\b${phrase}\\b` - : phrase, - !options.get("matchCase") ? "i" : "", - ) - } catch { - // TODO: add errors to state - } + const filteredPaths = operationFilterPredicate(spec.get("paths") || Map()) + const filteredTags = fn.extractTagsFromOperations(filteredPaths) + let schemaNamesForOperations = fn.extractSchemasFromOperations(filteredPaths, system) + const filteredOperationSchemas = spec + .getIn(schemaPathBase) + .filter((schema, name) => schemaNamesForOperations.includes(name)) + return partialSpecResult + .set("paths", filteredPaths) + .set("tags", filteredTags) + .setIn(schemaPathBase, filteredOperationSchemas) +} +export const advancedFilterMatcher_operations = (spec, options, phrase, { getSystem }) => { + const system = getSystem() + const expr = system.fn.getRegularFilterExpr(options, escapeRegExp(phrase)) if (expr) { - const filteredPaths = (spec.get("paths") || Map()) - .filter((path, pathName) => expr.test(pathName)) - const filteredTags = filteredPaths - .flatMap(method => method.map(op => op.get("tags"))) - let schemaNamesForOperations = Set() - filteredPaths.forEach((path) => { - path.forEach(op => { - schemaNamesForOperations = schemaNamesForOperations - .union(op - .getIn(["requestBody", "content"], Map()) - .map(v => v.getIn(["schema", "$ref"], v.getIn(["schema", "items", "$ref"]))) - .filter(ref => ref != null) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - .union(op - .getIn(["parameters"], List()) - .filter(param => param.get("in") === "body") - .map(v => v.getIn(["schema", "$ref"], v.getIn(["schema", "items", "$ref"]))) - .filter(ref => ref != null) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - .union(op - .getIn(["responses"], Map()) - .map(v => isOAS3 ? v.get("content") : v) - .filter(content => content != null) - .map(content => content.getIn(["schema", "$ref"], content.getIn(["schema", "items", "$ref"]))) - .filter(ref => ref != null) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - }) - }) - const filteredOperationSchemas = spec - .getIn(schemaPathBase) - .filter((schema, name) => schemaNamesForOperations.includes(name)) - return partialSpecResult - .set("paths", filteredPaths) - .set("tags", filteredTags) - .setIn(schemaPathBase, filteredOperationSchemas) + return system.fn.getMatchedOperationsSpec( + (ops) => ops.filter((path, pathName) => expr.test(pathName)), + spec, options, phrase, system, + ) } - return partialSpecResult } diff --git a/src/core/plugins/advanced-filter/fn/matchers/tag-matcher.js b/src/core/plugins/advanced-filter/fn/matchers/tag-matcher.js index e0c9df888944..e65b6e0b3822 100644 --- a/src/core/plugins/advanced-filter/fn/matchers/tag-matcher.js +++ b/src/core/plugins/advanced-filter/fn/matchers/tag-matcher.js @@ -1,95 +1,18 @@ -import { fromJS, List, Map, Set } from "immutable" // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping const escapeRegExp = (string) => { return string.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&") // $& means the whole matched string } /* eslint-disable camelcase */ -/* eslint-disable no-useless-escape */ -export const advancedFilterMatcher_tags = (spec, options, phrase, { specSelectors }) => { - const isOAS3 = specSelectors.isOAS3() - const schemaPathBaseRegex = isOAS3 - ? "\\/components\\/schemas\\/" - : "\\/definitions\\/" - const schemaPathBase = isOAS3 - ? ["components", "schemas"] - : ["definitions"] - const partialSpecResult = fromJS(isOAS3 - ? { paths: {}, tags: [], components: { schemas: {} } } - : { paths: {}, tags: [], definitions: {} }) - const nameMatcher = new RegExp(`(?<=${schemaPathBaseRegex}).*?(?=(?=\/)|$)`) - - phrase = escapeRegExp(phrase) - let expr - try { - expr = new RegExp( - options.get("matchWholeWord") - ? `\\b${phrase}\\b` - : phrase, - !options.get("matchCase") ? "i" : "", - ) - } catch { - // TODO: add errors to state - } +export const advancedFilterMatcher_tags = (spec, options, phrase, { getSystem }) => { + const system = getSystem() + const expr = system.fn.getRegularFilterExpr(options, escapeRegExp(phrase)) if (expr) { - const filteredPaths = (spec.get("paths") || Map()) - .map(path => path + return system.fn.getMatchedOperationsSpec( + (ops) => ops.map(path => path .filter(op => op.get("tags").filter(tag => expr.test(tag)).count() > 0), - ) - .filter(method => method.count() > 0) - const filteredTags = filteredPaths - .flatMap(method => method.map(op => op.get("tags"))) - let schemaNamesForOperations = Set() - filteredPaths.forEach((path) => { - path.forEach(op => { - schemaNamesForOperations = schemaNamesForOperations - .union(op - .getIn(["requestBody", "content"], Map()) - .map(v => v.getIn(["schema", "$ref"], v.getIn(["schema", "items", "$ref"]))) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - .union(op - .getIn(["parameters"], List()) - .filter(param => param.get("in") === "body") - .map(v => v.getIn(["schema", "$ref"], v.getIn(["schema", "items", "$ref"]))) - .filter(ref => ref != null) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - .union(op - .getIn(["responses"], Map()) - .map(v => isOAS3 ? v.get("content") : v) - .filter(content => content != null) - .map(content => content.getIn(["schema", "$ref"], content.getIn(["schema", "items", "$ref"]))) - .filter(ref => ref != null) - .valueSeq() - .toSet() - .map(requestBodyRef => { - const nameMatch = nameMatcher.exec(requestBodyRef) - return nameMatch ? nameMatch[0] : nameMatch - }) - .filter(name => name != null), - ) - }) - }) - const filteredOperationSchemas = spec - .getIn(schemaPathBase) - .filter((schema, name) => schemaNamesForOperations.includes(name)) - return partialSpecResult - .set("paths", filteredPaths) - .set("tags", filteredTags) - .setIn(schemaPathBase, filteredOperationSchemas) + ), + spec, options, phrase, system, + ) } - return partialSpecResult } diff --git a/src/core/plugins/advanced-filter/fn/regular-filter-expr.js b/src/core/plugins/advanced-filter/fn/regular-filter-expr.js new file mode 100644 index 000000000000..77c07c321752 --- /dev/null +++ b/src/core/plugins/advanced-filter/fn/regular-filter-expr.js @@ -0,0 +1,14 @@ +export const getRegularFilterExpr = (options, phrase) => { + let expr + try { + expr = new RegExp( + options.get("matchWholeWord") + ? `\\b${phrase}\\b` + : phrase, + !options.get("matchCase") ? "i" : "", + ) + } catch { + // TODO: add errors to state + } + return expr +}