k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/result.go (about) 1 // Copyright 2015 go-swagger maintainers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package validate 16 17 import ( 18 "fmt" 19 "strings" 20 21 "k8s.io/kube-openapi/pkg/validation/errors" 22 ) 23 24 // Result represents a validation result set, composed of 25 // errors and warnings. 26 // 27 // It is used to keep track of all detected errors and warnings during 28 // the validation of a specification. 29 // 30 // Matchcount is used to determine 31 // which errors are relevant in the case of AnyOf, OneOf 32 // schema validation. Results from the validation branch 33 // with most matches get eventually selected. 34 // 35 // TODO: keep path of key originating the error 36 type Result struct { 37 Errors []error 38 Warnings []error 39 MatchCount int 40 } 41 42 // Merge merges this result with the other one(s), preserving match counts etc. 43 func (r *Result) Merge(others ...*Result) *Result { 44 for _, other := range others { 45 if other != nil { 46 r.AddErrors(other.Errors...) 47 r.AddWarnings(other.Warnings...) 48 r.MatchCount += other.MatchCount 49 } 50 } 51 return r 52 } 53 54 // MergeAsErrors merges this result with the other one(s), preserving match counts etc. 55 // 56 // Warnings from input are merged as Errors in the returned merged Result. 57 func (r *Result) MergeAsErrors(others ...*Result) *Result { 58 for _, other := range others { 59 if other != nil { 60 r.AddErrors(other.Errors...) 61 r.AddErrors(other.Warnings...) 62 r.MatchCount += other.MatchCount 63 } 64 } 65 return r 66 } 67 68 // MergeAsWarnings merges this result with the other one(s), preserving match counts etc. 69 // 70 // Errors from input are merged as Warnings in the returned merged Result. 71 func (r *Result) MergeAsWarnings(others ...*Result) *Result { 72 for _, other := range others { 73 if other != nil { 74 r.AddWarnings(other.Errors...) 75 r.AddWarnings(other.Warnings...) 76 r.MatchCount += other.MatchCount 77 } 78 } 79 return r 80 } 81 82 // AddErrors adds errors to this validation result (if not already reported). 83 // 84 // Since the same check may be passed several times while exploring the 85 // spec structure (via $ref, ...) reported messages are kept 86 // unique. 87 func (r *Result) AddErrors(errors ...error) { 88 for _, e := range errors { 89 found := false 90 if e != nil { 91 for _, isReported := range r.Errors { 92 if e.Error() == isReported.Error() { 93 found = true 94 break 95 } 96 } 97 if !found { 98 r.Errors = append(r.Errors, e) 99 } 100 } 101 } 102 } 103 104 // AddWarnings adds warnings to this validation result (if not already reported). 105 func (r *Result) AddWarnings(warnings ...error) { 106 for _, e := range warnings { 107 found := false 108 if e != nil { 109 for _, isReported := range r.Warnings { 110 if e.Error() == isReported.Error() { 111 found = true 112 break 113 } 114 } 115 if !found { 116 r.Warnings = append(r.Warnings, e) 117 } 118 } 119 } 120 } 121 122 func (r *Result) keepRelevantErrors() *Result { 123 // TODO: this one is going to disapear... 124 // keepRelevantErrors strips a result from standard errors and keeps 125 // the ones which are supposedly more accurate. 126 // 127 // The original result remains unaffected (creates a new instance of Result). 128 // This method is used to work around the "matchCount" filter which would otherwise 129 // strip our result from some accurate error reporting from lower level validators. 130 // 131 // NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor 132 // very efficient. On the other hand, relying on go-openapi/errors to manipulate 133 // codes would require to change a lot here. So, for the moment, let's go with 134 // placeholders. 135 strippedErrors := []error{} 136 for _, e := range r.Errors { 137 if strings.HasPrefix(e.Error(), "IMPORTANT!") { 138 strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!"))) 139 } 140 } 141 strippedWarnings := []error{} 142 for _, e := range r.Warnings { 143 if strings.HasPrefix(e.Error(), "IMPORTANT!") { 144 strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!"))) 145 } 146 } 147 strippedResult := new(Result) 148 strippedResult.Errors = strippedErrors 149 strippedResult.Warnings = strippedWarnings 150 return strippedResult 151 } 152 153 // IsValid returns true when this result is valid. 154 // 155 // Returns true on a nil *Result. 156 func (r *Result) IsValid() bool { 157 if r == nil { 158 return true 159 } 160 return len(r.Errors) == 0 161 } 162 163 // HasErrors returns true when this result is invalid. 164 // 165 // Returns false on a nil *Result. 166 func (r *Result) HasErrors() bool { 167 if r == nil { 168 return false 169 } 170 return !r.IsValid() 171 } 172 173 // HasWarnings returns true when this result contains warnings. 174 // 175 // Returns false on a nil *Result. 176 func (r *Result) HasWarnings() bool { 177 if r == nil { 178 return false 179 } 180 return len(r.Warnings) > 0 181 } 182 183 // HasErrorsOrWarnings returns true when this result contains 184 // either errors or warnings. 185 // 186 // Returns false on a nil *Result. 187 func (r *Result) HasErrorsOrWarnings() bool { 188 if r == nil { 189 return false 190 } 191 return len(r.Errors) > 0 || len(r.Warnings) > 0 192 } 193 194 // Inc increments the match count 195 func (r *Result) Inc() { 196 r.MatchCount++ 197 } 198 199 // AsError renders this result as an error interface 200 // 201 // TODO: reporting / pretty print with path ordered and indented 202 func (r *Result) AsError() error { 203 if r.IsValid() { 204 return nil 205 } 206 return errors.CompositeValidationError(r.Errors...) 207 }