Skip to content

Commit c242a0e

Browse files
committed
Merge branch 'master' of github.com:swagger-api/swagger-editor
2 parents 3fe2852 + 0ce28b9 commit c242a0e

File tree

11 files changed

+756
-279
lines changed

11 files changed

+756
-279
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"immutable": "^3.x.x",
4949
"js-yaml": "^3.5.5",
5050
"json-beautify": "^1.0.1",
51+
"json-refs": "^3.0.4",
5152
"prop-types": "15.6.0",
5253
"react": "^15.6.2",
5354
"react-addons-css-transition-group": "^15.4.2",

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ const defaults = {
4646
components: {
4747
EditorLayout
4848
},
49-
showExtensions: true
49+
showExtensions: true,
50+
swagger2GeneratorUrl: "https://generator.swagger.io/api/swagger.json",
51+
oas3GeneratorUrl: "http://generator3.swagger.io/api/generator.json"
5052
}
5153

5254
module.exports = function SwaggerEditor(options) {

src/plugins/validate-semantic/validators/2and3/paths.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import {
2+
checkForDefinition,
3+
PATH_TEMPLATES_REGEX
4+
} from "../helpers"
5+
16
export const validate2And3PathParameterKeysDontContainQuestionMarks = () => system => {
27
return system.validateSelectors
38
.allPathItems()
@@ -14,3 +19,41 @@ export const validate2And3PathParameterKeysDontContainQuestionMarks = () => syst
1419
}, [])
1520
})
1621
}
22+
23+
export const validate2And3PathParameterDeclarationHasMatchingDefiniton = () => async system => {
24+
const nodes = await system.validateSelectors.allPathItems()
25+
26+
return nodes.reduce(async (prev, node) => {
27+
const acc = await prev
28+
const pathTemplates = (node.key.match(PATH_TEMPLATES_REGEX) || [])
29+
.map(str => str.replace("{", "").replace("}", ""))
30+
if(pathTemplates.length) {
31+
for (let paramName of pathTemplates) {
32+
if(paramName.length === 0) {
33+
// don't validate empty param names... they're invalid anyway
34+
continue
35+
}
36+
const resolverResult = await system.fn.memoizedResolveSubtree(system.specSelectors.specJson(), node.path)
37+
const res = checkForDefinition(paramName, resolverResult.spec)
38+
if(res.inOperation && res.missingFromOperations.length) {
39+
const missingStr = res.missingFromOperations
40+
.map(str => `"${str}"`)
41+
.join(", ")
42+
43+
acc.push({
44+
message: `Declared path parameter "${paramName}" needs to be defined within every operation in the path (missing in ${missingStr}), or moved to the path-level parameters object`,
45+
path: [...node.path],
46+
level: "error",
47+
})
48+
} else if(!res.found) {
49+
acc.push({
50+
message: `Declared path parameter "${paramName}" needs to be defined as a path parameter at either the path or operation level`,
51+
path: [...node.path],
52+
level: "error",
53+
})
54+
}
55+
}
56+
}
57+
return acc
58+
}, Promise.resolve([]))
59+
}

src/plugins/validate-semantic/validators/2and3/refs.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import get from "lodash/get"
22
import { escapeJsonPointerToken } from "../../../refs-util"
3+
import { pathFromPtr } from "json-refs"
34

45
export const validate2And3RefHasNoSiblings = () => system => {
56
return system.validateSelectors.all$refs()
@@ -66,7 +67,6 @@ export const validate2And3RefPathFormatting = () => (system) => {
6667

6768
if(refPath && refPath[0] !== "/") {
6869
errors.push({
69-
// $ref instead of $$ref
7070
path: [...node.path.slice(0, -1), "$ref"],
7171
message: "$ref paths must begin with `#/`",
7272
level: "error"
@@ -78,3 +78,29 @@ export const validate2And3RefPathFormatting = () => (system) => {
7878
return errors
7979
})
8080
}
81+
82+
export const validate2And3RefPointersExist = () => (system) => {
83+
const json = system.specSelectors.specJson()
84+
return system.validateSelectors.all$refs()
85+
.then((refs) => {
86+
const errors = []
87+
88+
refs.forEach((node) => {
89+
const value = node.node
90+
if(typeof value === "string" && value[0] === "#") {
91+
// if pointer starts with "#", it is a local ref
92+
const path = pathFromPtr(value)
93+
94+
if(json.getIn(path) === undefined) {
95+
errors.push({
96+
path: [...node.path.slice(0, -1), "$ref"],
97+
message: "$refs must reference a valid location in the document",
98+
level: "error"
99+
})
100+
}
101+
}
102+
})
103+
104+
return errors
105+
})
106+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const operationKeys = ["get", "post", "put", "delete", "options", "head", "patch"]
2+
3+
export const PATH_TEMPLATES_REGEX = /\{(.*?)\}/g
4+
5+
export function checkForDefinition(paramName, pathItem) {
6+
const pathItemParameters = pathItem.parameters
7+
const operationsInPathItem = (Object.keys(pathItem) || [])
8+
.filter(key => operationKeys.indexOf(key) > -1)
9+
.map(key => {
10+
const obj = pathItem[key]
11+
obj.method = key
12+
return obj
13+
})
14+
15+
const res = {
16+
found: false,
17+
inPath: false,
18+
inOperation: false,
19+
missingFromOperations: []
20+
}
21+
22+
// Look at the path parameters
23+
if(Array.isArray(pathItemParameters)) {
24+
pathItemParameters.forEach(param => {
25+
if(param.name === paramName && param.in === "path") {
26+
res.found = true
27+
res.inPath = true
28+
}
29+
})
30+
}
31+
32+
// Next, look at the operations...
33+
if(!res.found && operationsInPathItem.length) {
34+
operationsInPathItem
35+
.forEach(op => {
36+
const inThisOperation = (op.parameters || [])
37+
.some(param => param.name === paramName && param.in === "path")
38+
39+
if(inThisOperation) {
40+
res.found = true
41+
res.inOperation = true
42+
}
43+
44+
if(!inThisOperation) {
45+
res.missingFromOperations.push(op.method)
46+
}
47+
})
48+
}
49+
50+
return res
51+
}
Lines changed: 3 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,6 @@
1-
const operationKeys = ["get", "post", "put", "delete", "options", "head", "patch"]
2-
3-
const PATH_TEMPLATES_REGEX = /\{(.*?)\}/g
4-
5-
export const validatePathParameterDeclarationHasMatchingDefiniton = () => async system => {
6-
const nodes = await system.validateSelectors.allPathItems()
7-
8-
return nodes.reduce(async (prev, node) => {
9-
const acc = await prev
10-
const pathTemplates = (node.key.match(PATH_TEMPLATES_REGEX) || [])
11-
.map(str => str.replace("{", "").replace("}", ""))
12-
if(pathTemplates.length) {
13-
for (let paramName of pathTemplates) {
14-
if(paramName.length === 0) {
15-
// don't validate empty param names... they're invalid anyway
16-
continue
17-
}
18-
const resolverResult = await system.fn.memoizedResolveSubtree(system.specSelectors.specJson(), node.path)
19-
const res = checkForDefinition(paramName, resolverResult.spec)
20-
if(res.inOperation && res.missingFromOperations.length) {
21-
const missingStr = res.missingFromOperations
22-
.map(str => `"${str}"`)
23-
.join(", ")
24-
25-
acc.push({
26-
message: `Declared path parameter "${paramName}" needs to be defined within every operation in the path (missing in ${missingStr}), or moved to the path-level parameters object`,
27-
path: [...node.path],
28-
level: "error",
29-
})
30-
} else if(!res.found) {
31-
acc.push({
32-
message: `Declared path parameter "${paramName}" needs to be defined as a path parameter at either the path or operation level`,
33-
path: [...node.path],
34-
level: "error",
35-
})
36-
}
37-
}
38-
}
39-
return acc
40-
}, Promise.resolve([]))
41-
}
1+
import {
2+
PATH_TEMPLATES_REGEX
3+
} from "./helpers"
424

435
export const validatePathParameterDeclarationIsNotEmpty = () => system => {
446
return system.validateSelectors
@@ -81,53 +43,3 @@ export const validatePathParameterKeysAreDifferent = () => system => {
8143
}, [])
8244
})
8345
}
84-
85-
/// Helpers
86-
87-
function checkForDefinition(paramName, pathItem) {
88-
const pathItemParameters = pathItem.parameters
89-
const operationsInPathItem = (Object.keys(pathItem) || [])
90-
.filter(key => operationKeys.indexOf(key) > -1)
91-
.map(key => {
92-
const obj = pathItem[key]
93-
obj.method = key
94-
return obj
95-
})
96-
97-
const res = {
98-
found: false,
99-
inPath: false,
100-
inOperation: false,
101-
missingFromOperations: []
102-
}
103-
104-
// Look at the path parameters
105-
if(Array.isArray(pathItemParameters)) {
106-
pathItemParameters.forEach(param => {
107-
if(param.name === paramName && param.in === "path") {
108-
res.found = true
109-
res.inPath = true
110-
}
111-
})
112-
}
113-
114-
// Next, look at the operations...
115-
if(!res.found && operationsInPathItem.length) {
116-
operationsInPathItem
117-
.forEach(op => {
118-
const inThisOperation = (op.parameters || [])
119-
.some(param => param.name === paramName && param.in === "path")
120-
121-
if(inThisOperation) {
122-
res.found = true
123-
res.inOperation = true
124-
}
125-
126-
if(!inThisOperation) {
127-
res.missingFromOperations.push(op.method)
128-
}
129-
})
130-
}
131-
132-
return res
133-
}

0 commit comments

Comments
 (0)