mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: improve dynamic parameter validation errors (#18501)
`BuildError` response from `wsbuilder` does not support rich errors from validation. Changed this to use the `Validations` block of codersdk responses to return all errors for invalid parameters.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
// Package httperror handles formatting and writing some sentinel errors returned
|
||||
// within coder to the API.
|
||||
// This package exists outside httpapi to avoid some cyclic dependencies
|
||||
package httperror
|
||||
@@ -0,0 +1,91 @@
|
||||
package httperror
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/dynamicparameters"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/wsbuilder"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func WriteWorkspaceBuildError(ctx context.Context, rw http.ResponseWriter, err error) {
|
||||
var buildErr wsbuilder.BuildError
|
||||
if errors.As(err, &buildErr) {
|
||||
if httpapi.IsUnauthorizedError(err) {
|
||||
buildErr.Status = http.StatusForbidden
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, buildErr.Status, codersdk.Response{
|
||||
Message: buildErr.Message,
|
||||
Detail: buildErr.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var parameterErr *dynamicparameters.ResolverError
|
||||
if errors.As(err, ¶meterErr) {
|
||||
resp := codersdk.Response{
|
||||
Message: "Unable to validate parameters",
|
||||
Validations: nil,
|
||||
}
|
||||
|
||||
// Sort the parameter names so that the order is consistent.
|
||||
sortedNames := make([]string, 0, len(parameterErr.Parameter))
|
||||
for name := range parameterErr.Parameter {
|
||||
sortedNames = append(sortedNames, name)
|
||||
}
|
||||
sort.Strings(sortedNames)
|
||||
|
||||
for _, name := range sortedNames {
|
||||
diag := parameterErr.Parameter[name]
|
||||
resp.Validations = append(resp.Validations, codersdk.ValidationError{
|
||||
Field: name,
|
||||
Detail: DiagnosticsErrorString(diag),
|
||||
})
|
||||
}
|
||||
|
||||
if parameterErr.Diagnostics.HasErrors() {
|
||||
resp.Detail = DiagnosticsErrorString(parameterErr.Diagnostics)
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, resp)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error creating workspace build.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
func DiagnosticError(d *hcl.Diagnostic) string {
|
||||
return fmt.Sprintf("%s; %s", d.Summary, d.Detail)
|
||||
}
|
||||
|
||||
func DiagnosticsErrorString(d hcl.Diagnostics) string {
|
||||
count := len(d)
|
||||
switch {
|
||||
case count == 0:
|
||||
return "no diagnostics"
|
||||
case count == 1:
|
||||
return DiagnosticError(d[0])
|
||||
default:
|
||||
for _, d := range d {
|
||||
// Render the first error diag.
|
||||
// If there are warnings, do not priority them over errors.
|
||||
if d.Severity == hcl.DiagError {
|
||||
return fmt.Sprintf("%s, and %d other diagnostic(s)", DiagnosticError(d), count-1)
|
||||
}
|
||||
}
|
||||
|
||||
// All warnings? ok...
|
||||
return fmt.Sprintf("%s, and %d other diagnostic(s)", DiagnosticError(d[0]), count-1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user