in internal/api/validate.go [219:345]
func ValidateRequest(validate *validator.Validate, method string, resource any) []arm.CloudErrorBody {
var errorDetails []arm.CloudErrorBody
err := validate.Struct(validateContext{Method: method, Resource: resource})
if err == nil {
return nil
}
// Convert validation errors to cloud error details.
switch err := err.(type) {
case validator.ValidationErrors:
for _, fieldErr := range err {
message := fmt.Sprintf("Invalid value '%v' for field '%s'", fieldErr.Value(), fieldErr.Field())
// Try to add a corrective suggestion to the message.
tag := fieldErr.Tag()
if strings.HasPrefix(tag, "enum_") {
if len(strings.Split(fieldErr.Param(), " ")) == 1 {
message += fmt.Sprintf(" (must be %s)", fieldErr.Param())
} else {
message += fmt.Sprintf(" (must be one of: %s)", fieldErr.Param())
}
} else {
switch tag {
case "api_version": // custom tag
message = fmt.Sprintf("Unrecognized API version '%s'", fieldErr.Value())
case "openshift_version": // custom tag
message = fmt.Sprintf("Invalid OpenShift version '%s'", fieldErr.Value())
case "pem_certificates": // custom tag
message += " (must provide PEM encoded certificates)"
case "k8s_label_value": // custom tag
// Rerun the label value validation to obtain the error message.
if value, ok := fieldErr.Value().(string); ok {
errList := k8svalidation.IsValidLabelValue(value)
message += fmt.Sprintf(" (%s)", strings.Join(errList, "; "))
}
case "k8s_qualified_name": // custom tag
// Rerun the qualified name validation to obtain the error message.
if value, ok := fieldErr.Value().(string); ok {
errList := k8svalidation.IsQualifiedName(value)
message += fmt.Sprintf(" (%s)", strings.Join(errList, "; "))
}
case "required", "required_for_put": // custom tag
message = fmt.Sprintf("Missing required field '%s'", fieldErr.Field())
case "required_unless":
// The parameter format is pairs of "fieldName fieldValue".
// Multiple pairs are possible but we currently only use one.
fields := strings.Fields(fieldErr.Param())
if len(fields) > 1 {
// We want to print the JSON name for the field
// referenced in the parameter, but FieldError does
// not provide access to the parent reflect.Type from
// which we could look it up. So approximate the JSON
// name by lowercasing the first letter.
message = fmt.Sprintf("Field '%s' is required when '%s' is not '%s'", fieldErr.Field(), approximateJSONName(fields[0]), fields[1])
}
case "resource_id": // custom tag
if fieldErr.Param() != "" {
message += fmt.Sprintf(" (must be a valid '%s' resource ID)", fieldErr.Param())
} else {
message += " (must be a valid Azure resource ID)"
}
case "cidrv4":
message += " (must be a v4 CIDR range)"
case "dns_rfc1035_label":
message += " (must be a valid DNS RFC 1035 label)"
case "excluded_with":
// We want to print the JSON name for the field
// referenced in the parameter, but FieldError does
// not provide access to the parent reflect.Type from
// which we could look it up. So approximate the JSON
// name by lowercasing the first letter.
zero := reflect.Zero(fieldErr.Type()).Interface()
message = fmt.Sprintf("Field '%s' must be %v when '%s' is specified", fieldErr.Field(), zero, approximateJSONName(fieldErr.Param()))
case "gtefield":
// We want to print the JSON name for the field
// referenced in the parameter, but FieldError does
// not provide access to the parent reflect.Type from
// which we could look it up. So approximate the JSON
// name by lowercasing the first letter.
message += fmt.Sprintf(" (must be at least the value of '%s')", approximateJSONName(fieldErr.Param()))
case "ipv4":
message += " (must be an IPv4 address)"
case "max":
switch fieldErr.Kind() {
case reflect.String:
message += fmt.Sprintf(" (maximum length is %s)", fieldErr.Param())
default:
if fieldErr.Param() == "0" {
message += " (must be non-positive)"
} else {
message += fmt.Sprintf(" (must be at most %s)", fieldErr.Param())
}
}
case "min":
switch fieldErr.Kind() {
case reflect.String:
message += fmt.Sprintf(" (minimum length is %s)", fieldErr.Param())
default:
if fieldErr.Param() == "0" {
message += " (must be non-negative)"
} else {
message += fmt.Sprintf(" (must be at least %s)", fieldErr.Param())
}
}
case "startswith":
message += fmt.Sprintf(" (must start with '%s')", fieldErr.Param())
case "url":
message += " (must be a URL)"
}
}
errorDetails = append(errorDetails, arm.CloudErrorBody{
Code: arm.CloudErrorCodeInvalidRequestContent,
Message: message,
// Split "validateContext.Resource.{REMAINING_FIELDS}"
Target: strings.SplitN(fieldErr.Namespace(), ".", 3)[2],
})
}
default:
errorDetails = append(errorDetails, arm.CloudErrorBody{
Code: arm.CloudErrorCodeInvalidRequestContent,
Message: err.Error(),
})
}
return errorDetails
}