Skip to content

Commit

Permalink
refactor: matcher helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
mathis-m committed Jan 20, 2021
1 parent 1f34534 commit 2e82de2
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 182 deletions.
3 changes: 3 additions & 0 deletions dev-helpers/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
40 changes: 40 additions & 0 deletions src/core/plugins/advanced-filter/fn/helper/extract-schemas.js
Original file line number Diff line number Diff line change
@@ -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
}
4 changes: 4 additions & 0 deletions src/core/plugins/advanced-filter/fn/helper/extract-tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const extractTagsFromOperations = (operations) => {
return operations
.flatMap(method => method.map(op => op.get("tags")))
}
10 changes: 10 additions & 0 deletions src/core/plugins/advanced-filter/fn/helper/schema-path-base.js
Original file line number Diff line number Diff line change
@@ -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 }
}
12 changes: 11 additions & 1 deletion src/core/plugins/advanced-filter/fn/index.js
Original file line number Diff line number Diff line change
@@ -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
}
20 changes: 4 additions & 16 deletions src/core/plugins/advanced-filter/fn/matchers/definition-matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
102 changes: 22 additions & 80 deletions src/core/plugins/advanced-filter/fn/matchers/operation-matcher.js
Original file line number Diff line number Diff line change
@@ -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
}
93 changes: 8 additions & 85 deletions src/core/plugins/advanced-filter/fn/matchers/tag-matcher.js
Original file line number Diff line number Diff line change
@@ -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
}
14 changes: 14 additions & 0 deletions src/core/plugins/advanced-filter/fn/regular-filter-expr.js
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 2e82de2

Please sign in to comment.