-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
success_response.go
140 lines (114 loc) · 4.24 KB
/
success_response.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2020-2021 Dave Shanley / Quobix
// SPDX-License-Identifier: MIT
package openapi
import (
"fmt"
"github.com/daveshanley/vacuum/model"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"strconv"
)
// SuccessResponse is a rule that checks if an operation returns a code >= 200 and <= 400
type SuccessResponse struct {
}
// GetSchema returns a model.RuleFunctionSchema defining the schema of the SuccessResponse rule.
func (sr SuccessResponse) GetSchema() model.RuleFunctionSchema {
return model.RuleFunctionSchema{Name: "oasOpSuccessResponse"}
}
// GetCategory returns the category of the SuccessResponse rule.
func (sr SuccessResponse) GetCategory() string {
return model.FunctionCategoryOpenAPI
}
// RunRule will execute the SuccessResponse rule, based on supplied context and a supplied []*yaml.Node slice.
func (sr SuccessResponse) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) []model.RuleFunctionResult {
if len(nodes) <= 0 {
return nil
}
var results []model.RuleFunctionResult
var currentPath string
var currentVerb string
for _, n := range nodes {
//for j, operationNode := range n.Content {
_, pathNode := utils.FindKeyNode("paths", n.Content)
if pathNode == nil {
continue
}
for j, operationNode := range pathNode.Content {
if utils.IsNodeStringValue(operationNode) {
currentPath = operationNode.Value
}
if utils.IsNodeMap(operationNode) {
for h, verbMapNode := range operationNode.Content {
if utils.IsNodeStringValue(verbMapNode) && utils.IsHttpVerb(verbMapNode.Value) {
currentVerb = verbMapNode.Value
} else {
continue
}
verbDataNode := operationNode.Content[h+1]
fieldNode, valNode := utils.FindKeyNodeTop(context.RuleAction.Field, verbDataNode.Content)
if fieldNode != nil && valNode != nil {
var responseSeen bool
var responseInvalidType bool
var responseCode int
var invalidCodes []int
for _, response := range valNode.Content {
if utils.IsNodeStringValue(response) {
responseCode, _ = strconv.Atoi(response.Value)
if responseCode >= 200 && responseCode < 400 {
responseSeen = true
}
}
// check for an integer instead of a string, and check if this is an OpenAPI 3+ doc,
// if so, throw an error about using the wrong type
// https://github.com/daveshanley/vacuum/issues/214
if context.SpecInfo.SpecType == utils.OpenApi3 {
if utils.IsNodeIntValue(response) {
responseInvalidType = true
responseSeen = true
c, _ := strconv.Atoi(response.Value)
invalidCodes = append(invalidCodes, c)
}
}
}
if !responseSeen || responseInvalidType {
// see if we can extract a name from the operationId
_, g := utils.FindKeyNode("operationId", verbDataNode.Content)
var name string
if g != nil {
name = g.Value
} else {
name = "undefined operation (no operationId)"
}
endNode := utils.FindLastChildNodeWithLevel(valNode, 0)
if endNode == nil && j+1 < len(operationNode.Content) {
endNode = operationNode.Content[j+1]
}
if !responseSeen {
results = append(results, model.RuleFunctionResult{
Message: fmt.Sprintf("operation `%s` must define at least a single `2xx` or `3xx` response", name),
StartNode: fieldNode,
EndNode: &yaml.Node{Line: fieldNode.Line, Column: fieldNode.Column + len(fieldNode.Value), Kind: fieldNode.Kind, Value: fieldNode.Value},
Path: fmt.Sprintf("$.paths['%s'].%s.%s", currentPath, currentVerb, context.RuleAction.Field),
Rule: context.Rule,
})
}
if responseInvalidType {
for i := range invalidCodes {
results = append(results, model.RuleFunctionResult{
Message: fmt.Sprintf("operation `%s` uses an `integer` instead of a `string` "+
"for response code `%d`", name, invalidCodes[i]),
StartNode: fieldNode,
EndNode: fieldNode,
Path: fmt.Sprintf("$.paths['%s'].%s.%s", currentPath, currentVerb, context.RuleAction.Field),
Rule: context.Rule,
})
}
}
}
}
}
}
}
}
return results
}