func ValidateRequest()

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
}