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  }