chore: Update BE http errors to be ui friendly (#1994)

* chore: More UI friendly errors

Mainly capitlization + messages prefix error
This commit is contained in:
Steven Masley 2022-06-03 16:48:09 -05:00 committed by GitHub
parent 847e2b18da
commit c9a4642a12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 544 additions and 317 deletions

View File

@ -55,7 +55,7 @@ func autostartShow() *cobra.Command {
validSchedule, err := schedule.Weekly(*workspace.AutostartSchedule)
if err != nil {
// This should never happen.
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "invalid autostart schedule %q for workspace %s: %s\n", *workspace.AutostartSchedule, workspace.Name, err.Error())
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Invalid autostart schedule %q for workspace %s: %s\n", *workspace.AutostartSchedule, workspace.Name, err.Error())
return nil
}

View File

@ -108,7 +108,7 @@ func TestAutostart(t *testing.T) {
clitest.SetupConfig(t, client, root)
err := cmd.Execute()
require.ErrorContains(t, err, "status code 403: forbidden", "unexpected error")
require.ErrorContains(t, err, "status code 403: Forbidden", "unexpected error")
})
t.Run("Disable_NotFound", func(t *testing.T) {
@ -125,7 +125,7 @@ func TestAutostart(t *testing.T) {
clitest.SetupConfig(t, client, root)
err := cmd.Execute()
require.ErrorContains(t, err, "status code 403: forbidden", "unexpected error")
require.ErrorContains(t, err, "status code 403: Forbidden", "unexpected error")
})
t.Run("Enable_DefaultSchedule", func(t *testing.T) {

View File

@ -149,7 +149,7 @@ func TestTTL(t *testing.T) {
clitest.SetupConfig(t, client, root)
err := cmd.Execute()
require.ErrorContains(t, err, "status code 403: forbidden", "unexpected error")
require.ErrorContains(t, err, "status code 403: Forbidden", "unexpected error")
})
t.Run("Unset_NotFound", func(t *testing.T) {
@ -166,6 +166,6 @@ func TestTTL(t *testing.T) {
clitest.SetupConfig(t, client, root)
err := cmd.Execute()
require.ErrorContains(t, err, "status code 403: forbidden", "unexpected error")
require.ErrorContains(t, err, "status code 403: Forbidden", "unexpected error")
})
}

View File

@ -21,9 +21,7 @@ func (api *API) Authorize(rw http.ResponseWriter, r *http.Request, action rbac.A
roles := httpmw.AuthorizationUserRoles(r)
err := api.Authorizer.ByRoleName(r.Context(), roles.ID.String(), roles.Roles, action, object.RBACObject())
if err != nil {
httpapi.Write(rw, http.StatusForbidden, httpapi.Response{
Message: err.Error(),
})
httpapi.Forbidden(rw)
// Log the errors for debugging
internalError := new(rbac.UnauthorizedError)

View File

@ -23,7 +23,8 @@ func (api *API) logReportCSPViolations(rw http.ResponseWriter, r *http.Request)
if err != nil {
api.Logger.Warn(ctx, "csp violation", slog.Error(err))
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "failed to read body",
Message: "Failed to read body, invalid json",
Detail: err.Error(),
})
return
}

View File

@ -32,7 +32,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
case "application/x-tar":
default:
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("unsupported content type: %s", contentType),
Message: fmt.Sprintf("Unsupported content type header %q", contentType),
})
return
}
@ -41,7 +41,8 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("read file: %s", err),
Message: "Failed to read file from request",
Detail: err.Error(),
})
return
}
@ -64,7 +65,8 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert file: %s", err),
Message: "Internal error saving file",
Detail: err.Error(),
})
return
}
@ -78,7 +80,7 @@ func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) {
hash := chi.URLParam(r, "hash")
if hash == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "hash must be provided",
Message: "File hash must be provided in url",
})
return
}
@ -89,7 +91,8 @@ func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get file: %s", err),
Message: "Internal error fetching file",
Detail: err.Error(),
})
return
}

View File

@ -1,7 +1,6 @@
package coderd
import (
"fmt"
"net/http"
"github.com/coder/coder/coderd/database"
@ -22,7 +21,8 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
privateKey, publicKey, err := gitsshkey.Generate(api.SSHKeygenAlgorithm)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("regenerate key pair: %s", err),
Message: "Internal error generating a new SSH keypair",
Detail: err.Error(),
})
return
}
@ -35,7 +35,8 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update git SSH key: %s", err),
Message: "Internal error updating user's git SSH key",
Detail: err.Error(),
})
return
}
@ -43,7 +44,8 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) {
newKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get git SSH key: %s", err),
Message: "Internal error fetching user's git SSH key",
Detail: err.Error(),
})
return
}
@ -67,7 +69,8 @@ func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update git SSH key: %s", err),
Message: "Internal error fetching user's SSH key",
Detail: err.Error(),
})
return
}
@ -86,7 +89,8 @@ func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("getting workspace resources: %s", err),
Message: "Internal error fetching workspace resource",
Detail: err.Error(),
})
return
}
@ -94,7 +98,8 @@ func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetWorkspaceBuildByJobID(r.Context(), resource.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("getting workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
@ -102,7 +107,8 @@ func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
workspace, err := api.Database.GetWorkspaceByID(r.Context(), job.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("getting workspace: %s", err),
Message: "Internal error fetching workspace",
Detail: err.Error(),
})
return
}
@ -110,7 +116,8 @@ func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) {
gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), workspace.OwnerID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("getting git SSH key: %s", err),
Message: "Internal error fetching git SSH key",
Detail: err.Error(),
})
return
}

View File

@ -52,8 +52,22 @@ func init() {
// Response represents a generic HTTP response.
type Response struct {
Message string `json:"message" validate:"required"`
Errors []Error `json:"errors,omitempty" validate:"required"`
// Message is an actionable message that depicts actions the request took.
// These messages should be fully formed sentences with proper punctuation.
// Examples:
// - "A user has been created."
// - "Failed to create a user."
Message string `json:"message"`
// Detail is a debug message that provides further insight into why the
// action failed. This information can be technical and a regular golang
// err.Error() text.
// - "database: too many open connections"
// - "stat: too many open files"
Detail string `json:"detail"`
// Validations are form field-specific friendly error messages. They will be
// shown on a form field in the UI. These can also be used to add additional
// context if there is a set of errors in the primary 'Message'.
Validations []Error `json:"errors,omitempty"`
}
// Error represents a scoped error to a user input.
@ -64,7 +78,7 @@ type Error struct {
func Forbidden(rw http.ResponseWriter) {
Write(rw, http.StatusForbidden, Response{
Message: "forbidden",
Message: "Forbidden",
})
}
@ -93,7 +107,8 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
err := json.NewDecoder(r.Body).Decode(value)
if err != nil {
Write(rw, http.StatusBadRequest, Response{
Message: fmt.Sprintf("read body: %s", err.Error()),
Message: "Request body must be valid JSON",
Detail: err.Error(),
})
return false
}
@ -108,14 +123,15 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
})
}
Write(rw, http.StatusBadRequest, Response{
Message: "Validation failed",
Errors: apiErrors,
Message: "Validation failed",
Validations: apiErrors,
})
return false
}
if err != nil {
Write(rw, http.StatusInternalServerError, Response{
Message: fmt.Sprintf("validation: %s", err.Error()),
Message: "Internal error validating request body payload",
Detail: err.Error(),
})
return false
}

View File

@ -74,9 +74,9 @@ func TestRead(t *testing.T) {
var v httpapi.Response
err := json.NewDecoder(rw.Body).Decode(&v)
require.NoError(t, err)
require.Len(t, v.Errors, 1)
require.Equal(t, "value", v.Errors[0].Field)
require.Equal(t, "Validation failed for tag \"required\" with value: \"\"", v.Errors[0].Detail)
require.Len(t, v.Validations, 1)
require.Equal(t, "value", v.Validations[0].Field)
require.Equal(t, "Validation failed for tag \"required\" with value: \"\"", v.Validations[0].Detail)
})
}

View File

@ -65,7 +65,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
}
if cookieValue == "" {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie or query parameter must be provided", SessionTokenKey),
Message: fmt.Sprintf("Cookie %q or query parameter must be provided", SessionTokenKey),
})
return
}
@ -73,7 +73,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// APIKeys are formatted: ID-SECRET
if len(parts) != 2 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key format", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie API key format", SessionTokenKey),
})
return
}
@ -82,13 +82,13 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Ensuring key lengths are valid.
if len(keyID) != 10 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key id", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie API key id", SessionTokenKey),
})
return
}
if len(keySecret) != 22 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key secret", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie API key secret", SessionTokenKey),
})
return
}
@ -96,12 +96,13 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "api key is invalid",
Message: "API key is invalid",
})
return
}
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get api key by id: %s", err.Error()),
Message: "Internal error fetching API key by id",
Detail: err.Error(),
})
return
}
@ -110,7 +111,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Checking to see if the secret is valid.
if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "api key secret is invalid",
Message: "API key secret is invalid",
})
return
}
@ -127,7 +128,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
oauthConfig = oauth.Github
default:
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("unexpected authentication type %q", key.LoginType),
Message: fmt.Sprintf("Unexpected authentication type %q", key.LoginType),
})
return
}
@ -139,7 +140,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
}).Token()
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("couldn't refresh expired oauth token: %s", err.Error()),
Message: "Could not refresh expired Oauth token",
Detail: err.Error(),
})
return
}
@ -154,7 +156,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Checking if the key is expired.
if key.ExpiresAt.Before(now) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("api key expired at %q", key.ExpiresAt.String()),
Message: fmt.Sprintf("API key expired at %q", key.ExpiresAt.String()),
})
return
}
@ -182,7 +184,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("api key couldn't update: %s", err.Error()),
Message: fmt.Sprintf("API key couldn't update: %s", err.Error()),
})
return
}
@ -194,14 +196,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "roles not found",
Message: "Internal error fetching user's roles",
Detail: err.Error(),
})
return
}
if roles.Status != database.UserStatusActive {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("user is not active (status = %q), contact an admin to reactivate your account", roles.Status),
Message: fmt.Sprintf("User is not active (status = %q). Contact an admin to reactivate your account.", roles.Status),
})
return
}

View File

@ -15,7 +15,9 @@ func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID
rawID := chi.URLParam(r, param)
if rawID == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("%q must be provided", param),
Message: "Missing UUID in URL",
// Url params mean nothing to a user
Detail: fmt.Sprintf("%q URL param missing", param),
})
return uuid.UUID{}, false
}
@ -23,7 +25,8 @@ func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID
parsed, err := uuid.Parse(rawID)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("%q must be a uuid", param),
Message: fmt.Sprintf("Invalid UUID %q", param),
Detail: err.Error(),
})
return uuid.UUID{}, false
}

View File

@ -63,7 +63,8 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
state, err := cryptorand.String(32)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("generate state string: %s", err),
Message: "Internal error generating state string",
Detail: err.Error(),
})
return
}
@ -91,7 +92,7 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
if state == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "state must be provided",
Message: "State must be provided",
})
return
}
@ -99,13 +100,13 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
stateCookie, err := r.Cookie(oauth2StateCookieName)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie must be provided", oauth2StateCookieName),
Message: fmt.Sprintf("Cookie %q must be provided", oauth2StateCookieName),
})
return
}
if stateCookie.Value != state {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "state mismatched",
Message: "State mismatched",
})
return
}
@ -119,7 +120,8 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
oauthToken, err := config.Exchange(r.Context(), code)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("exchange oauth code: %s", err),
Message: "Internal error exchanging Oauth code",
Detail: err.Error(),
})
return
}

View File

@ -46,13 +46,14 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
organization, err := db.GetOrganizationByID(r.Context(), orgID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("organization %q does not exist", orgID),
Message: fmt.Sprintf("Organization %q does not exist", orgID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization: %s", err.Error()),
Message: "Internal error fetching organization",
Detail: err.Error(),
})
return
}
@ -76,13 +77,14 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusForbidden, httpapi.Response{
Message: "not a member of the organization",
Message: "Not a member of the organization",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization member: %s", err.Error()),
Message: "Internal error fetching organization member",
Detail: err.Error(),
})
return
}

View File

@ -35,19 +35,20 @@ func ExtractTemplateParam(db database.Store) func(http.Handler) http.Handler {
template, err := db.GetTemplateByID(r.Context(), templateID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template %q does not exist", templateID),
Message: fmt.Sprintf("Template %q does not exist", templateID),
})
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template",
Detail: err.Error(),
})
return
}
if template.Deleted {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template %q does not exist", templateID),
Message: fmt.Sprintf("Template %q does not exist", templateID),
})
return
}

View File

@ -35,13 +35,14 @@ func ExtractTemplateVersionParam(db database.Store) func(http.Handler) http.Hand
templateVersion, err := db.GetTemplateVersionByID(r.Context(), templateVersionID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template version %q does not exist", templateVersionID),
Message: fmt.Sprintf("Template version %q does not exist", templateVersionID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err.Error()),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}

View File

@ -2,7 +2,6 @@ package httpmw
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
@ -50,7 +49,8 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
user, err = db.GetUserByID(r.Context(), APIKey(r).UserID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err.Error()),
Message: "Internal error fetching user",
Detail: err.Error(),
})
return
}

View File

@ -31,14 +31,14 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
cookie, err := r.Cookie(SessionTokenKey)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie must be provided", SessionTokenKey),
Message: fmt.Sprintf("Cookie %q must be provided", SessionTokenKey),
})
return
}
token, err := uuid.Parse(cookie.Value)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("parse token %q: %s", cookie.Value, err),
Message: fmt.Sprintf("Parse token %q: %s", cookie.Value, err),
})
return
}
@ -46,14 +46,15 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "agent token is invalid",
Message: "Agent token is invalid",
})
return
}
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace agent: %s", err),
Message: "Internal error fetching workspace agent",
Detail: err.Error(),
})
return
}

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"net/http"
"github.com/coder/coder/coderd/database"
@ -33,20 +32,22 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl
agent, err := db.GetWorkspaceAgentByID(r.Context(), agentUUID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "agent doesn't exist with that id",
Message: "Agent doesn't exist with that id",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get agent: %s", err),
Message: "Internal error fetching workspace agent",
Detail: err.Error(),
})
return
}
resource, err := db.GetWorkspaceResourceByID(r.Context(), agent.ResourceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get resource: %s", err),
Message: "Internal error fetching workspace resource",
Detail: err.Error(),
})
return
}
@ -54,7 +55,8 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl
job, err := db.GetProvisionerJobByID(r.Context(), resource.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -67,14 +69,16 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl
build, err := db.GetWorkspaceBuildByJobID(r.Context(), job.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
workspace, err := db.GetWorkspaceByID(r.Context(), build.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace: %s", err),
Message: "Internal error fetching workspace",
Detail: err.Error(),
})
return
}
@ -82,7 +86,7 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl
apiKey := APIKey(r)
if apiKey.UserID != workspace.OwnerID {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "getting non-personal agents isn't supported",
Message: "Getting non-personal agents isn't supported",
})
return
}

View File

@ -35,13 +35,14 @@ func ExtractWorkspaceBuildParam(db database.Store) func(http.Handler) http.Handl
workspaceBuild, err := db.GetWorkspaceBuildByID(r.Context(), workspaceBuildID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("workspace build %q does not exist", workspaceBuildID),
Message: fmt.Sprintf("Workspace build %q does not exist", workspaceBuildID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err.Error()),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}

View File

@ -33,13 +33,14 @@ func ExtractWorkspaceParam(db database.Store) func(http.Handler) http.Handler {
workspace, err := db.GetWorkspaceByID(r.Context(), workspaceID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("workspace %q does not exist", workspaceID),
Message: fmt.Sprintf("Workspace %q does not exist", workspaceID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace: %s", err.Error()),
Message: "Internal error fetching workspace",
Detail: err.Error(),
})
return
}

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"errors"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
@ -35,13 +34,14 @@ func ExtractWorkspaceResourceParam(db database.Store) func(http.Handler) http.Ha
resource, err := db.GetWorkspaceResourceByID(r.Context(), resourceUUID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "resource doesn't exist with that id",
Message: "Resource doesn't exist with that id",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner resource: %s", err),
Message: "Internal error fetching provisioner resource",
Detail: err.Error(),
})
return
}
@ -49,7 +49,8 @@ func ExtractWorkspaceResourceParam(db database.Store) func(http.Handler) http.Ha
job, err := db.GetProvisionerJobByID(r.Context(), resource.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error provisioner job",
Detail: err.Error(),
})
return
}
@ -62,7 +63,8 @@ func ExtractWorkspaceResourceParam(db database.Store) func(http.Handler) http.Ha
build, err := db.GetWorkspaceBuildByJobID(r.Context(), job.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error workspace build",
Detail: err.Error(),
})
return
}

View File

@ -76,11 +76,11 @@ func (api *API) updateOrganizationMemberRoles(ctx context.Context, args database
roleOrg, err := uuid.Parse(orgID)
if err != nil {
return database.OrganizationMember{}, xerrors.Errorf("role must have proper uuids for organization, %q does not", r)
return database.OrganizationMember{}, xerrors.Errorf("Role must have proper UUIDs for organization, %q does not", r)
}
if roleOrg != args.OrgID {
return database.OrganizationMember{}, xerrors.Errorf("must only pass roles for org %q", args.OrgID.String())
return database.OrganizationMember{}, xerrors.Errorf("Must only pass roles for org %q", args.OrgID.String())
}
if _, err := rbac.RoleByName(r); err != nil {
@ -90,7 +90,7 @@ func (api *API) updateOrganizationMemberRoles(ctx context.Context, args database
updatedUser, err := api.Database.UpdateMemberRoles(ctx, args)
if err != nil {
return database.OrganizationMember{}, xerrors.Errorf("update site roles: %w", err)
return database.OrganizationMember{}, xerrors.Errorf("Update site roles: %w", err)
}
return updatedUser, nil
}

View File

@ -45,13 +45,14 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
_, err := api.Database.GetOrganizationByName(r.Context(), req.Name)
if err == nil {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: "organization already exists with that name",
Message: "Organization already exists with that name",
})
return
}
if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization: %s", err.Error()),
Message: fmt.Sprintf("Internal error fetching organization %q", req.Name),
Detail: err.Error(),
})
return
}
@ -83,7 +84,8 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error inserting organization member",
Detail: err.Error(),
})
return
}

View File

@ -1,7 +1,6 @@
package coderd
import (
"fmt"
"net/http"
"strconv"
@ -25,7 +24,10 @@ func parsePagination(w http.ResponseWriter, r *http.Request) (p codersdk.Paginat
afterID, err = uuid.Parse(r.URL.Query().Get("after_id"))
if err != nil {
httpapi.Write(w, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("after_id must be a valid uuid: %s", err.Error()),
Message: "Query param 'after_id' must be a valid UUID",
Validations: []httpapi.Error{
{Field: "after_id", Detail: err.Error()},
},
})
return p, false
}
@ -34,7 +36,10 @@ func parsePagination(w http.ResponseWriter, r *http.Request) (p codersdk.Paginat
limit, err = strconv.Atoi(s)
if err != nil {
httpapi.Write(w, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("limit must be an integer: %s", err.Error()),
Message: "Query param 'limit' must be a valid integer",
Validations: []httpapi.Error{
{Field: "limit", Detail: err.Error()},
},
})
return p, false
}
@ -43,7 +48,10 @@ func parsePagination(w http.ResponseWriter, r *http.Request) (p codersdk.Paginat
offset, err = strconv.Atoi(s)
if err != nil {
httpapi.Write(w, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("offset must be an integer: %s", err.Error()),
Message: "Query param 'offset' must be a valid integer",
Validations: []httpapi.Error{
{Field: "offset", Detail: err.Error()},
},
})
return p, false
}

View File

@ -41,13 +41,14 @@ func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) {
})
if err == nil {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: fmt.Sprintf("a parameter already exists in scope %q with name %q", scope, createRequest.Name),
Message: fmt.Sprintf("Parameter already exists in scope %q and name %q", scope, createRequest.Name),
})
return
}
if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get parameter value: %s", err),
Message: "Internal error fetching parameter",
Detail: err.Error(),
})
return
}
@ -65,7 +66,8 @@ func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert parameter value: %s", err),
Message: "Internal error inserting parameter",
Detail: err.Error(),
})
return
}
@ -96,7 +98,8 @@ func (api *API) parameters(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get parameter values by scope: %s", err),
Message: "Internal error fetching parameter scope values",
Detail: err.Error(),
})
return
}
@ -130,20 +133,23 @@ func (api *API) deleteParameter(rw http.ResponseWriter, r *http.Request) {
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("parameter doesn't exist in the provided scope with name %q", name),
Message: fmt.Sprintf("No parameter found at the provided scope with name %q", name),
Detail: err.Error(),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get parameter value: %s", err),
Message: "Internal error fetching parameter",
Detail: err.Error(),
})
return
}
err = api.Database.DeleteParameterValueByID(r.Context(), parameterValue.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("delete parameter: %s", err),
Message: "Internal error deleting parameter",
Detail: err.Error(),
})
return
}
@ -210,13 +216,8 @@ func (api *API) parameterRBACResource(rw http.ResponseWriter, r *http.Request, s
resource, err = api.Database.GetWorkspaceByID(ctx, scopeID)
case database.ParameterScopeTemplate:
resource, err = api.Database.GetTemplateByID(ctx, scopeID)
case database.ParameterScopeImportJob:
// This scope does not make sense from this api.
// ImportJob params are created with the job, and the job id cannot
// be predicted.
err = xerrors.Errorf("ImportJob scope not supported")
default:
err = xerrors.Errorf("scope %q unsupported", scope)
err = xerrors.Errorf("Parameter scope %q unsupported", scope)
}
// Write error payload to rw if we cannot find the resource for the scope
@ -225,7 +226,7 @@ func (api *API) parameterRBACResource(rw http.ResponseWriter, r *http.Request, s
httpapi.Forbidden(rw)
} else {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("param scope resource: %s", err.Error()),
Message: err.Error(),
})
}
return nil, false
@ -242,7 +243,10 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter
scope = database.ParameterScopeWorkspace
default:
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("invalid scope %q", scope),
Message: fmt.Sprintf("Invalid scope %q", scope),
Validations: []httpapi.Error{
{Field: "scope", Detail: "invalid scope"},
},
})
return scope, uuid.Nil, false
}
@ -251,7 +255,11 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter
uid, err := uuid.Parse(id)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("invalid uuid %q: %s", id, err),
Message: fmt.Sprintf("Invalid UUID %q", id),
Detail: err.Error(),
Validations: []httpapi.Error{
{Field: "id", Detail: "Invalid UUID"},
},
})
return scope, uuid.Nil, false
}

View File

@ -2,11 +2,12 @@ package coderd_test
import (
"context"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"net/http"
"testing"
"github.com/coder/coder/provisioner/echo"
"github.com/coder/coder/provisionersdk/proto"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/coderdtest"

View File

@ -38,7 +38,8 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner daemons: %s", err),
Message: "Internal error fetching provisioner daemons",
Detail: err.Error(),
})
return
}

View File

@ -32,7 +32,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
beforeRaw := r.URL.Query().Get("before")
if beforeRaw != "" && follow {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "before cannot be used with follow",
Message: "Query param \"before\" cannot be used with \"follow\"",
})
return
}
@ -43,7 +43,10 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
afterMS, err := strconv.ParseInt(afterRaw, 10, 64)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("unable to parse after %q: %s", afterRaw, err),
Message: "Query param \"after\" must be an integer",
Validations: []httpapi.Error{
{Field: "after", Detail: "Must be an integer"},
},
})
return
}
@ -59,7 +62,10 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
beforeMS, err := strconv.ParseInt(beforeRaw, 10, 64)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("unable to parse before %q: %s", beforeRaw, err),
Message: "Query param \"before\" must be an integer",
Validations: []httpapi.Error{
{Field: "before", Detail: "Must be an integer"},
},
})
return
}
@ -79,7 +85,8 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner logs: %s", err),
Message: "Internal error fetching provisioner logs",
Detail: err.Error(),
})
return
}
@ -112,7 +119,8 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("subscribe to provisioner job logs: %s", err),
Message: "Internal error watching provisioner logs",
Detail: err.Error(),
})
return
}
@ -128,7 +136,8 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprint("get provisioner job logs: %w", err),
Message: "Internal error fetching provisioner logs",
Detail: err.Error(),
})
return
}
@ -191,7 +200,8 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job resources: %s", err),
Message: "Internal error fetching job resources",
Detail: err.Error(),
})
return
}
@ -205,7 +215,8 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace agents by resources: %s", err),
Message: "Internal error fetching workspace agent",
Detail: err.Error(),
})
return
}
@ -220,7 +231,8 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request,
apiAgent, err := convertWorkspaceAgent(agent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert provisioner job agent: %s", err),
Message: "Internal error reading job agent",
Detail: err.Error(),
})
return
}

View File

@ -1,6 +1,7 @@
package coderd
import (
"fmt"
"net/http"
"github.com/coder/coder/coderd/httpmw"
@ -60,7 +61,7 @@ func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) {
for k, v := range params.Checks {
if v.Object.ResourceType == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "'resource_type' must be defined",
Message: fmt.Sprintf("Object's \"resource_type\" field must be defined for key %q", k),
})
return
}

View File

@ -112,7 +112,7 @@ func TestListRoles(t *testing.T) {
})
require.NoError(t, err, "create org")
const forbidden = "forbidden"
const forbidden = "Forbidden"
siteRoles := convertRoles(rbac.RoleAdmin(), "auditor")
orgRoles := convertRoles(rbac.RoleOrgAdmin(admin.OrganizationID))

View File

@ -27,7 +27,8 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace counts: %s", err.Error()),
Message: "Internal error fetching workspace count",
Detail: err.Error(),
})
return
}
@ -58,7 +59,8 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspaces by template id: %s", err),
Message: "Internal error fetching workspaces by template id",
Detail: err.Error(),
})
return
}
@ -74,7 +76,8 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update template deleted by id: %s", err),
Message: "Internal error deleting template",
Detail: err.Error(),
})
return
}
@ -100,8 +103,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
})
if err == nil {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: fmt.Sprintf("template %q already exists", createTemplate.Name),
Errors: []httpapi.Error{{
Message: fmt.Sprintf("Template with name %q already exists", createTemplate.Name),
Validations: []httpapi.Error{{
Field: "name",
Detail: "This value is already in use and should be unique.",
}},
@ -110,27 +113,33 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
}
if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template by name: %s", err),
Message: "Internal error fetching template by name",
Detail: err.Error(),
})
return
}
templateVersion, err := api.Database.GetTemplateVersionByID(r.Context(), createTemplate.VersionID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "template version does not exist",
Message: fmt.Sprintf("Template version %q does not exist", createTemplate.VersionID),
Validations: []httpapi.Error{
{Field: "template_version_id", Detail: "Template version does not exist"},
},
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version by id: %s", err),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}
importJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get import job by id: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -185,7 +194,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error inserting template",
Detail: err.Error(),
})
return
}
@ -203,7 +213,8 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get templates: %s", err.Error()),
Message: "Internal error fetching templates in organization",
Detail: err.Error(),
})
return
}
@ -222,7 +233,8 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request)
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace counts: %s", err.Error()),
Message: "Internal error fetching workspace counts",
Detail: err.Error(),
})
return
}
@ -240,13 +252,14 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("no template found by name %q in the %q organization", templateName, organization.Name),
Message: fmt.Sprintf("No template found by name %q in the %q organization", templateName, organization.Name),
})
return
}
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template by organization and name: %s", err),
Message: "Internal error fetching template",
Detail: err.Error(),
})
return
}
@ -261,7 +274,8 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace counts: %s", err.Error()),
Message: "Internal error fetching workspace counts",
Detail: err.Error(),
})
return
}

View File

@ -29,7 +29,8 @@ func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -46,7 +47,8 @@ func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -71,7 +73,8 @@ func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update provisioner job: %s", err),
Message: "Internal error updating provisioner job",
Detail: err.Error(),
})
return
}
@ -89,7 +92,8 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -105,7 +109,8 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("list parameter schemas: %s", err),
Message: "Internal error listing parameter schemas",
Detail: err.Error(),
})
return
}
@ -114,7 +119,8 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) {
apiSchema, err := convertParameterSchema(schema)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert: %s", err),
Message: fmt.Sprintf("Internal error converting schema %s", schema.Name),
Detail: err.Error(),
})
return
}
@ -133,7 +139,8 @@ func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Reques
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -153,7 +160,8 @@ func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Reques
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("compute values: %s", err),
Message: "Internal error computing values",
Detail: err.Error(),
})
return
}
@ -185,7 +193,8 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error updating provisioner job",
Detail: err.Error(),
})
return
}
@ -218,8 +227,9 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
ParameterValues: parameterValues,
})
if err != nil {
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
Message: fmt.Sprintf("marshal new provisioner job: %s", err),
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "Internal error unmarshalling provisioner job",
Detail: err.Error(),
})
return
}
@ -240,7 +250,8 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert provisioner job: %s", err),
Message: "Internal error inserting provisioner job",
Detail: err.Error(),
})
return
}
@ -309,7 +320,8 @@ func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update provisioner job: %s", err),
Message: "Internal error updating provisioner job",
Detail: err.Error(),
})
return
}
@ -331,7 +343,8 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re
jobUUID, err := uuid.Parse(jobID)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "Job ID must be a valid UUID",
Message: fmt.Sprintf("Job ID %q must be a valid UUID", jobID),
Detail: err.Error(),
})
return database.ProvisionerJob{}, false
}
@ -343,7 +356,8 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job by ID %q: %s", jobUUID.String(), err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return database.ProvisionerJob{}, false
}
@ -362,7 +376,8 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re
err = json.Unmarshal(job.Input, &input)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("unmarshal job metadata: %s", err),
Message: "Internal error unmarshaling job metadata",
Detail: err.Error(),
})
return database.ProvisionerJob{}, false
}
@ -394,12 +409,13 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
_, err := store.GetTemplateVersionByID(r.Context(), paginationParams.AfterID)
if err != nil && xerrors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("record at \"after_id\" (%q) does not exists", paginationParams.AfterID.String()),
Message: fmt.Sprintf("Record at \"after_id\" (%q) does not exists", paginationParams.AfterID.String()),
})
return err
} else if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version at after_id: %s", err),
Message: "Internal error fetching template version at after_id",
Detail: err.Error(),
})
return err
}
@ -417,7 +433,8 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err),
Message: "Internal error fetching template versions",
Detail: err.Error(),
})
return err
}
@ -429,7 +446,8 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
jobs, err := store.GetProvisionerJobsByIDs(r.Context(), jobIDs)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get jobs: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return err
}
@ -442,7 +460,7 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
job, exists := jobByID[version.JobID.String()]
if !exists {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("job %q doesn't exist for version %q", version.JobID, version.ID),
Message: fmt.Sprintf("Job %q doesn't exist for version %q", version.JobID, version.ID),
})
return err
}
@ -474,20 +492,22 @@ func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("no template version found by name %q", templateVersionName),
Message: fmt.Sprintf("No template version found by name %q", templateVersionName),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version by name: %s", err),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -508,13 +528,14 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
version, err := api.Database.GetTemplateVersionByID(r.Context(), req.ID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "template version not found",
Message: "Template version not found",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}
@ -530,7 +551,8 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update active template version: %s", err),
Message: "Internal error updating active template version",
Detail: err.Error(),
})
return
}
@ -552,13 +574,14 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
_, err := api.Database.GetTemplateByID(r.Context(), req.TemplateID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "template does not exist",
Message: "Template does not exist",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template",
Detail: err.Error(),
})
return
}
@ -567,13 +590,14 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
file, err := api.Database.GetFileByHash(r.Context(), req.StorageSource)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "file not found",
Message: "File not found",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get file: %s", err),
Message: "Internal error fetching file",
Detail: err.Error(),
})
return
}
@ -671,7 +695,8 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -691,7 +716,8 @@ func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}

View File

@ -42,7 +42,8 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
memberships, err := api.GithubOAuth2Config.ListOrganizationMemberships(r.Context(), oauthClient)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get authenticated github user organizations: %s", err),
Message: "Internal error fetching authenticated Github user organizations",
Detail: err.Error(),
})
return
}
@ -66,7 +67,8 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
emails, err := api.GithubOAuth2Config.ListEmails(r.Context(), oauthClient)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get personal github user: %s", err),
Message: "Internal error fetching personal Github user",
Detail: err.Error(),
})
return
}
@ -86,7 +88,8 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user by email: %s", err),
Message: fmt.Sprintf("Internal error fetching user by email %q", *email.Email),
Detail: err.Error(),
})
return
}
@ -119,7 +122,8 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
ghUser, err := api.GithubOAuth2Config.AuthenticatedUser(r.Context(), oauthClient)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get authenticated github user: %s", err),
Message: "Internal error fetching authenticated Github user",
Detail: err.Error(),
})
return
}
@ -144,7 +148,8 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("create user: %s", err),
Message: "Internal error creating user",
Detail: err.Error(),
})
return
}

View File

@ -30,7 +30,8 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) {
userCount, err := api.Database.GetUserCount(r.Context())
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user count: %s", err.Error()),
Message: "Internal error fetching user count",
Detail: err.Error(),
})
return
}
@ -58,7 +59,8 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
userCount, err := api.Database.GetUserCount(r.Context())
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user count: %s", err.Error()),
Message: "Internal error fetching user count",
Detail: err.Error(),
})
return
}
@ -66,7 +68,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
// If a user already exists, the initial admin user no longer can be created.
if userCount != 0 {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: "the initial user has already been created",
Message: "The initial user has already been created",
})
return
}
@ -78,7 +80,8 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error creating user",
Detail: err.Error(),
})
return
}
@ -93,7 +96,8 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error updating user's roles",
Detail: err.Error(),
})
return
}
@ -121,6 +125,9 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
default:
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("%q is not a valid user status", filter),
Validations: []httpapi.Error{
{Field: "status", Detail: "invalid status"},
},
})
return
}
@ -150,7 +157,8 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error fetching users",
Detail: err.Error(),
})
return
}
@ -165,7 +173,8 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -204,13 +213,14 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
})
if err == nil {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: "user already exists",
Message: "User already exists",
})
return
}
if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err),
Message: "Internal error fetching user",
Detail: err.Error(),
})
return
}
@ -218,13 +228,14 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
_, err = api.Database.GetOrganizationByID(r.Context(), createUser.OrganizationID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "organization does not exist with the provided id",
Message: fmt.Sprintf("Organization does not exist with the provided id %q", createUser.OrganizationID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization: %s", err),
Message: "Internal error fetching organization",
Detail: err.Error(),
})
return
}
@ -232,7 +243,8 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
user, _, err := api.createUser(r.Context(), createUser)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error creating user",
Detail: err.Error(),
})
return
}
@ -252,7 +264,8 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -285,14 +298,15 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
})
}
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: "user already exists",
Errors: responseErrors,
Message: "User already exists",
Validations: responseErrors,
})
return
}
if !errors.Is(err, sql.ErrNoRows) && isDifferentUser {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err),
Message: "Internal error fetching user",
Detail: err.Error(),
})
return
}
@ -306,7 +320,8 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("patch user: %s", err.Error()),
Message: "Internal error updating user",
Detail: err.Error(),
})
return
}
@ -314,7 +329,8 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -346,7 +362,8 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("put user suspended: %s", err.Error()),
Message: fmt.Sprintf("Internal error updating user's status to %q", status),
Detail: err.Error(),
})
return
}
@ -354,7 +371,8 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
organizations, err := userOrganizationIDs(r.Context(), api, user)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -381,7 +399,8 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
err := userpassword.Validate(params.Password)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Errors: []httpapi.Error{
Message: "Invalid password",
Validations: []httpapi.Error{
{
Field: "password",
Detail: err.Error(),
@ -398,13 +417,15 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
ok, err := userpassword.Compare(string(user.HashedPassword), params.OldPassword)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("compare user password: %s", err.Error()),
Message: "Internal error with passwords",
Detail: err.Error(),
})
return
}
if !ok {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Errors: []httpapi.Error{
Message: "Old password is incorrect",
Validations: []httpapi.Error{
{
Field: "old_password",
Detail: "Old password is incorrect.",
@ -418,7 +439,8 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
hashedPassword, err := userpassword.Hash(params.Password)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("hash password: %s", err.Error()),
Message: "Internal error hashing new password",
Detail: err.Error(),
})
return
}
@ -428,7 +450,8 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("put user password: %s", err.Error()),
Message: "Internal error updating user's password",
Detail: err.Error(),
})
return
}
@ -452,7 +475,8 @@ func (api *API) userRoles(rw http.ResponseWriter, r *http.Request) {
memberships, err := api.Database.GetOrganizationMembershipsByUserID(r.Context(), user.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user memberships: %s", err),
Message: "Internal error fetching user's organization memberships",
Detail: err.Error(),
})
return
}
@ -518,7 +542,8 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
organizationIDs, err := userOrganizationIDs(r.Context(), api, user)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -532,7 +557,7 @@ func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUse
// Enforce only site wide roles.
for _, r := range args.GrantedRoles {
if _, ok := rbac.IsOrgRole(r); ok {
return database.User{}, xerrors.Errorf("must only update site wide roles")
return database.User{}, xerrors.Errorf("Must only update site wide roles")
}
if _, err := rbac.RoleByName(r); err != nil {
@ -558,7 +583,8 @@ func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organizations: %s", err.Error()),
Message: "Internal error fetching user's organizations",
Detail: err.Error(),
})
return
}
@ -610,7 +636,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
})
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err.Error()),
Message: "Internal error",
})
return
}
@ -619,7 +645,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
equal, err := userpassword.Compare(string(user.HashedPassword), loginWithPassword.Password)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("compare: %s", err.Error()),
Message: "Internal error",
})
}
if !equal {
@ -634,7 +660,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
// If the user logged into a suspended account, reject the login request.
if user.Status != database.UserStatusActive {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "You are suspended, contact an admin to reactivate your account",
Message: "Your account is suspended. Contact an admin to reactivate your account.",
})
return
}
@ -693,7 +719,8 @@ func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) {
err := api.Database.DeleteAPIKeyByID(r.Context(), apiKey.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("delete api key: %s", err.Error()),
Message: "Internal error deleting API key",
Detail: err.Error(),
})
return
}
@ -722,7 +749,8 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat
keyID, keySecret, err := generateAPIKeyIDSecret()
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("generate api key parts: %s", err.Error()),
Message: "Internal error generating API key",
Detail: err.Error(),
})
return "", false
}
@ -754,7 +782,8 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert api key: %s", err.Error()),
Message: "Internal error inserting API key",
Detail: err.Error(),
})
return "", false
}

View File

@ -103,7 +103,7 @@ func TestPostLogin(t *testing.T) {
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode())
require.Contains(t, apiErr.Message, "contact an admin")
require.Contains(t, apiErr.Message, "Contact an admin")
// Test a new session
_, err = client.LoginWithPassword(context.Background(), codersdk.LoginWithPasswordRequest{

View File

@ -17,7 +17,6 @@ import (
"nhooyr.io/websocket"
"cdr.dev/slog"
"github.com/coder/coder/agent"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
@ -35,7 +34,8 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert workspace agent: %s", err),
Message: "Internal error reading workspace agent",
Detail: err.Error(),
})
return
}
@ -53,7 +53,8 @@ func (api *API) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) {
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert workspace agent: %s", err),
Message: "Internal error reading workspace agent",
Detail: err.Error(),
})
return
}
@ -67,7 +68,8 @@ func (api *API) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) {
conn, err := websocket.Accept(rw, r, nil)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("accept websocket: %s", err),
Message: "Failed to accept websocket",
Detail: err.Error(),
})
return
}
@ -98,35 +100,40 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert workspace agent: %s", err),
Message: "Internal error reading workspace agent",
Detail: err.Error(),
})
return
}
resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), workspaceAgent.ResourceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace resource: %s", err),
Message: "Internal error fetching workspace resources",
Detail: err.Error(),
})
return
}
build, err := api.Database.GetWorkspaceBuildByJobID(r.Context(), resource.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
workspace, err := api.Database.GetWorkspaceByID(r.Context(), build.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace",
Detail: err.Error(),
})
return
}
owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace owner",
Detail: err.Error(),
})
return
}
@ -149,7 +156,8 @@ func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) {
resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), workspaceAgent.ResourceID)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("get workspace resource: %s", err),
Message: "Failed to accept websocket",
Detail: err.Error(),
})
return
}
@ -304,7 +312,8 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
host, port, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("get remote address: %s", err),
Message: "Invalid remote address",
Detail: err.Error(),
})
return
}
@ -312,7 +321,8 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
remoteAddress.Port, err = strconv.Atoi(port)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("remote address %q has no parsable port: %s", r.RemoteAddr, err),
Message: fmt.Sprintf("Port for remote address %q must be an integer.", r.RemoteAddr),
Detail: err.Error(),
})
return
}
@ -322,7 +332,8 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("accept websocket: %s", err),
Message: "Failed to accept websocket",
Detail: err.Error(),
})
return
}
@ -350,13 +361,14 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
apiAgent, err := convertWorkspaceAgent(workspaceAgent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert workspace agent: %s", err),
Message: "Internal error reading workspace agent",
Detail: err.Error(),
})
return
}
if apiAgent.Status != codersdk.WorkspaceAgentConnected {
httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{
Message: fmt.Sprintf("agent must be in the connected state: %s", apiAgent.Status),
Message: fmt.Sprintf("Agent state is %q, it must be in the %q state.", apiAgent.Status, codersdk.WorkspaceAgentConnected),
})
return
}
@ -364,7 +376,10 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
reconnect, err := uuid.Parse(r.URL.Query().Get("reconnect"))
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("reconnection must be a uuid: %s", err),
Message: "Query param 'reconnect' must be a valid UUID",
Validations: []httpapi.Error{
{Field: "reconnect", Detail: "invalid UUID"},
},
})
return
}
@ -382,7 +397,8 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("accept websocket: %s", err),
Message: "Failed to accept websocket",
Detail: err.Error(),
})
return
}

View File

@ -31,7 +31,8 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -62,12 +63,13 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
_, err := store.GetWorkspaceBuildByID(r.Context(), paginationParams.AfterID)
if err != nil && xerrors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("record at \"after_id\" (%q) does not exist", paginationParams.AfterID.String()),
Message: fmt.Sprintf("Record at \"after_id\" (%q) does not exist", paginationParams.AfterID.String()),
})
return err
} else if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build at after_id: %s", err),
Message: "Internal error fetching workspace build at \"after_id\"",
Detail: err.Error(),
})
return err
}
@ -85,7 +87,8 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace builds: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return err
}
@ -106,7 +109,8 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get jobs: %s", err),
Message: "Internal error fetching provisioner jobs",
Detail: err.Error(),
})
return
}
@ -120,7 +124,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
job, exists := jobByID[build.JobID.String()]
if !exists {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("job %q doesn't exist for build %q", build.JobID, build.ID),
Message: fmt.Sprintf("Job %q doesn't exist for build %q", build.JobID, build.ID),
})
return
}
@ -144,20 +148,22 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("no workspace build found by name %q", workspaceBuildName),
Message: fmt.Sprintf("No workspace build found by name %q", workspaceBuildName),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build by name: %s", err),
Message: "Internal error fetching workspace build by name",
Detail: err.Error(),
})
return
}
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -182,7 +188,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
action = rbac.ActionUpdate
default:
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("transition not supported: %q", createBuild.Transition),
Message: fmt.Sprintf("Transition %q not supported", createBuild.Transition),
})
return
}
@ -195,7 +201,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
latestBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get latest workspace build: %s", err),
Message: "Internal error fetching the latest workspace build",
Detail: err.Error(),
})
return
}
@ -204,8 +211,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
templateVersion, err := api.Database.GetTemplateVersionByID(r.Context(), createBuild.TemplateVersionID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "template version not found",
Errors: []httpapi.Error{{
Message: "Template version not found",
Validations: []httpapi.Error{{
Field: "template_version_id",
Detail: "template version not found",
}},
@ -214,14 +221,16 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}
templateVersionJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -247,7 +256,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
template, err := api.Database.GetTemplateByID(r.Context(), templateVersion.TemplateID.UUID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template job",
Detail: err.Error(),
})
return
}
@ -259,7 +269,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
priorJob, err := api.Database.GetProvisionerJobByID(r.Context(), priorHistory.JobID)
if err == nil && convertProvisionerJob(priorJob).Status.Active() {
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: "a workspace build is already active",
Message: "A workspace build is already active",
})
return
}
@ -267,7 +277,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
priorBuildNum = priorHistory.BuildNumber
} else if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get prior workspace build: %s", err),
Message: "Internal error fetching prior workspace build",
Detail: err.Error(),
})
return
}
@ -325,7 +336,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: err.Error(),
Message: "Internal error inserting workspace build",
Detail: err.Error(),
})
return
}
@ -339,7 +351,7 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "no workspace exists for this job",
Message: "No workspace exists for this job",
})
return
}
@ -352,7 +364,8 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -377,7 +390,8 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update provisioner job: %s", err),
Message: "Internal error updating provisioner job",
Detail: err.Error(),
})
return
}
@ -391,7 +405,7 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request)
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "no workspace exists for this job",
Message: "No workspace exists for this job",
})
return
}
@ -404,7 +418,8 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request)
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -416,7 +431,7 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "no workspace exists for this job",
Message: "No workspace exists for this job",
})
return
}
@ -429,7 +444,8 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -441,7 +457,7 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) {
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "no workspace exists for this job",
Message: "No workspace exists for this job",
})
return
}

View File

@ -26,7 +26,8 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r
instanceID, err := azureidentity.Validate(r.Context(), req.Signature, api.AzureCertificates)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("validate: %s", err),
Message: "Invalid Azure identity",
Detail: err.Error(),
})
return
}
@ -44,7 +45,8 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *
identity, err := awsidentity.Validate(req.Signature, req.Document, api.AWSCertificates)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("validate: %s", err),
Message: "Invalid AWS identity",
Detail: err.Error(),
})
return
}
@ -64,7 +66,8 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter,
payload, err := api.GoogleTokenValidator.Validate(r.Context(), req.JSONWebToken, "")
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("validate: %s", err),
Message: "Invalid GCP identity",
Detail: err.Error(),
})
return
}
@ -78,7 +81,8 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter,
err = mapstructure.Decode(payload.Claims, &claims)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("decode jwt claims: %s", err),
Message: "Error decoding JWT claims",
Detail: err.Error(),
})
return
}
@ -89,27 +93,30 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in
agent, err := api.Database.GetWorkspaceAgentByInstanceID(r.Context(), instanceID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("instance with id %q not found", instanceID),
Message: fmt.Sprintf("Instance with id %q not found", instanceID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job agent: %s", err),
Message: "Internal error fetching provisioner job agent",
Detail: err.Error(),
})
return
}
resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job resource: %s", err),
Message: "Internal error fetching provisioner job resource",
Detail: err.Error(),
})
return
}
job, err := api.Database.GetProvisionerJobByID(r.Context(), resource.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -123,14 +130,16 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in
err = json.Unmarshal(job.Input, &jobData)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("extract job data: %s", err),
Message: "Internal error extracting job data",
Detail: err.Error(),
})
return
}
resourceHistory, err := api.Database.GetWorkspaceBuildByID(r.Context(), jobData.WorkspaceBuildID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
@ -140,13 +149,14 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in
latestHistory, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), resourceHistory.WorkspaceID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get latest workspace build: %s", err),
Message: "Internal error fetching the latest workspace build",
Detail: err.Error(),
})
return
}
if latestHistory.ID != resourceHistory.ID {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("resource found for id %q, but isn't registered on the latest history", instanceID),
Message: fmt.Sprintf("Resource found for id %q, but isn't registered on the latest history", instanceID),
})
return
}

View File

@ -3,7 +3,6 @@ package coderd
import (
"database/sql"
"errors"
"fmt"
"net/http"
"github.com/google/uuid"
@ -25,7 +24,8 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) {
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
@ -41,7 +41,8 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) {
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job agents: %s", err),
Message: "Internal error fetching provisioner job agents",
Detail: err.Error(),
})
return
}
@ -50,7 +51,8 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) {
convertedAgent, err := convertWorkspaceAgent(agent, api.AgentConnectionUpdateFrequency)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("convert provisioner job agent: %s", err),
Message: "Internal error reading workspace agent",
Detail: err.Error(),
})
return
}

View File

@ -46,20 +46,23 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
showDeleted, err = strconv.ParseBool(deletedStr)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("invalid bool for 'deleted' query param: %s", err),
Message: fmt.Sprintf("Invalid boolean value %q for \"deleted\" query param", deletedStr),
Validations: []httpapi.Error{
{Field: "deleted", Detail: "Must be a valid boolean"},
},
})
return
}
}
if workspace.Deleted && !showDeleted {
httpapi.Write(rw, http.StatusGone, httpapi.Response{
Message: fmt.Sprintf("workspace %q was deleted, you can view this workspace by specifying '?deleted=true' and trying again", workspace.ID.String()),
Message: fmt.Sprintf("Workspace %q was deleted, you can view this workspace by specifying '?deleted=true' and trying again", workspace.ID.String()),
})
return
}
if !workspace.Deleted && showDeleted {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("workspace %q is not deleted, please remove '?deleted=true' and try again", workspace.ID.String()),
Message: fmt.Sprintf("Workspace %q is not deleted, please remove '?deleted=true' and try again", workspace.ID.String()),
})
return
}
@ -67,7 +70,8 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
@ -92,7 +96,8 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
err = group.Wait()
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("fetch resource: %s", err),
Message: "Internal error fetching resource",
Detail: err.Error(),
})
return
}
@ -142,7 +147,8 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
workspaces, err := api.Database.GetWorkspacesWithFilter(r.Context(), filter)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspaces for user: %s", err),
Message: "Internal error fetching workspaces",
Detail: err.Error(),
})
return
}
@ -175,7 +181,8 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace by name: %s", err),
Message: "Internal error fetching workspace by name",
Detail: err.Error(),
})
return
}
@ -186,21 +193,24 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
job, err := api.Database.GetProvisionerJobByID(r.Context(), build.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get provisioner job: %s", err),
Message: "Internal error fetching provisioner job",
Detail: err.Error(),
})
return
}
template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template",
Detail: err.Error(),
})
return
}
@ -225,8 +235,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
template, err := api.Database.GetTemplateByID(r.Context(), createWorkspace.TemplateID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("template %q doesn't exist", createWorkspace.TemplateID.String()),
Errors: []httpapi.Error{{
Message: fmt.Sprintf("Template %q doesn't exist", createWorkspace.TemplateID.String()),
Validations: []httpapi.Error{{
Field: "template_id",
Detail: "template not found",
}},
@ -235,14 +245,15 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template",
Detail: err.Error(),
})
return
}
if organization.ID != template.OrganizationID {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("template is not in organization %q", organization.Name),
Message: fmt.Sprintf("Template is not in organization %q", organization.Name),
})
return
}
@ -252,13 +263,14 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "you aren't allowed to access templates in that organization",
Message: "You aren't allowed to access templates in that organization",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization member: %s", err),
Message: "Internal error fetching organization member",
Detail: err.Error(),
})
return
}
@ -268,7 +280,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
_, err := schedule.Weekly(*createWorkspace.AutostartSchedule)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("parse autostart schedule: %s", err.Error()),
Message: "Error parsing autostart schedule",
Detail: err.Error(),
})
return
}
@ -279,8 +292,9 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "validate workspace ttl",
Errors: []httpapi.Error{
Message: "Invalid workspace TTL",
Detail: err.Error(),
Validations: []httpapi.Error{
{
Field: "ttl",
Detail: err.Error(),
@ -299,14 +313,15 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("find template for conflicting workspace name %q: %s", createWorkspace.Name, err),
Message: fmt.Sprintf("Find template for conflicting workspace name %q", createWorkspace.Name),
Detail: err.Error(),
})
return
}
// The template is fetched for clarity to the user on where the conflicting name may be.
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
Message: fmt.Sprintf("workspace %q already exists in the %q template", createWorkspace.Name, template.Name),
Errors: []httpapi.Error{{
Message: fmt.Sprintf("Workspace %q already exists in the %q template", createWorkspace.Name, template.Name),
Validations: []httpapi.Error{{
Field: "name",
Detail: "this value is already in use and should be unique",
}},
@ -315,7 +330,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
}
if !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace by name: %s", err.Error()),
Message: fmt.Sprintf("Internal error fetching workspace by name %q", createWorkspace.Name),
Detail: err.Error(),
})
return
}
@ -323,14 +339,16 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
templateVersion, err := api.Database.GetTemplateVersionByID(r.Context(), template.ActiveVersionID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err),
Message: "Internal error fetching template version",
Detail: err.Error(),
})
return
}
templateVersionJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version job: %s", err),
Message: "Internal error fetching template version job",
Detail: err.Error(),
})
return
}
@ -431,14 +449,16 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("create workspace: %s", err),
Message: "Internal error creating workspace",
Detail: err.Error(),
})
return
}
user, err := api.Database.GetUserByID(r.Context(), apiKey.UserID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err),
Message: "Internal error fetching user",
Detail: err.Error(),
})
return
}
@ -461,7 +481,8 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
dbSched, err := validWorkspaceSchedule(req.Schedule)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("invalid autostart schedule: %s", err),
Message: "Invalid autostart schedule",
Detail: err.Error(),
})
return
}
@ -472,7 +493,8 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update workspace autostart schedule: %s", err),
Message: "Internal error updating workspace autostart schedule",
Detail: err.Error(),
})
return
}
@ -493,8 +515,9 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
dbTTL, err := validWorkspaceTTLMillis(req.TTLMillis)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "validate workspace ttl",
Errors: []httpapi.Error{
Message: "Invalid workspace TTL",
Detail: err.Error(),
Validations: []httpapi.Error{
{
Field: "ttl",
Detail: err.Error(),
@ -510,7 +533,8 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("update workspace ttl: %s", err),
Message: "Internal error updating workspace TTL",
Detail: err.Error(),
})
return
}
@ -549,7 +573,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
if err := validWorkspaceDeadline(build.Deadline, newDeadline); err != nil {
code = http.StatusBadRequest
resp.Message = "bad extend workspace request"
resp.Errors = append(resp.Errors, httpapi.Error{Field: "deadline", Detail: err.Error()})
resp.Validations = append(resp.Validations, httpapi.Error{Field: "deadline", Detail: err.Error()})
return err
}
@ -622,14 +646,16 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspace.ID)
if err != nil {
_ = wsjson.Write(ctx, c, httpapi.Response{
Message: fmt.Sprintf("get workspace: %s", err),
Message: "Internal error fetching workspace",
Detail: err.Error(),
})
return
}
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
_ = wsjson.Write(ctx, c, httpapi.Response{
Message: fmt.Sprintf("get workspace build: %s", err),
Message: "Internal error fetching workspace build",
Detail: err.Error(),
})
return
}
@ -654,7 +680,8 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
err = group.Wait()
if err != nil {
_ = wsjson.Write(ctx, c, httpapi.Response{
Message: fmt.Sprintf("fetch resource: %s", err),
Message: "Internal error fetching resource",
Detail: err.Error(),
})
return
}

View File

@ -441,17 +441,17 @@ func TestWorkspaceUpdateAutostart(t *testing.T) {
{
name: "invalid location",
schedule: ptr.Ref("CRON_TZ=Imaginary/Place 30 9 * * 1-5"),
expectedError: "status code 500: invalid autostart schedule: parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place",
expectedError: "status code 500: Invalid autostart schedule\n\tError: parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place",
},
{
name: "invalid schedule",
schedule: ptr.Ref("asdf asdf asdf "),
expectedError: `status code 500: invalid autostart schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
expectedError: "status code 500: Invalid autostart schedule\n\tError: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix",
},
{
name: "only 3 values",
schedule: ptr.Ref("CRON_TZ=Europe/Dublin 30 9 *"),
expectedError: `status code 500: invalid autostart schedule: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix`,
expectedError: "status code 500: Invalid autostart schedule\n\tError: validate weekly schedule: expected schedule to consist of 5 fields with an optional CRON_TZ=<timezone> prefix",
},
}
@ -480,7 +480,7 @@ func TestWorkspaceUpdateAutostart(t *testing.T) {
})
if testCase.expectedError != "" {
require.ErrorContains(t, err, testCase.expectedError, "unexpected error when setting workspace autostart schedule")
require.ErrorContains(t, err, testCase.expectedError, "Invalid autostart schedule")
return
}
@ -521,7 +521,7 @@ func TestWorkspaceUpdateAutostart(t *testing.T) {
require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error")
coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint
require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404")
require.Equal(t, fmt.Sprintf("workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
require.Equal(t, fmt.Sprintf("Workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
})
}
@ -613,7 +613,7 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error")
coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint
require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404")
require.Equal(t, fmt.Sprintf("workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
require.Equal(t, fmt.Sprintf("Workspace %q does not exist", wsid), coderSDKErr.Message, "unexpected response code")
})
}

View File

@ -202,7 +202,10 @@ func (e *Error) Error() string {
if e.Helper != "" {
_, _ = fmt.Fprintf(&builder, ": %s", e.Helper)
}
for _, err := range e.Errors {
if e.Detail != "" {
_, _ = fmt.Fprintf(&builder, "\n\tError: %s", e.Detail)
}
for _, err := range e.Validations {
_, _ = fmt.Fprintf(&builder, "\n\t%s: %s", err.Field, err.Detail)
}
return builder.String()