From 540d8c14a066d4544520c33153a43c5da441ac7d Mon Sep 17 00:00:00 2001 From: sreya Date: Tue, 12 Jul 2022 21:42:16 +0000 Subject: [PATCH 1/6] ref: move httpapi.Reponse into codersdk --- cli/userlist_test.go | 2 +- coderd/coderd.go | 4 +- coderd/coderdtest/coderdtest.go | 2 +- coderd/csp.go | 3 +- coderd/files.go | 10 +-- coderd/files_test.go | 2 +- coderd/gitsshkey.go | 16 ++-- coderd/httpapi/httpapi.go | 42 +++------- coderd/httpapi/queryparams.go | 14 ++-- coderd/httpmw/apikey.go | 29 +++---- coderd/httpmw/apikey_test.go | 7 +- coderd/httpmw/httpmw.go | 5 +- coderd/httpmw/oauth2.go | 13 +-- coderd/httpmw/organizationparam.go | 5 +- coderd/httpmw/ratelimit.go | 3 +- coderd/httpmw/templateparam.go | 3 +- coderd/httpmw/templateversionparam.go | 3 +- coderd/httpmw/userparam.go | 9 ++- coderd/httpmw/workspaceagent.go | 9 ++- coderd/httpmw/workspaceagentparam.go | 13 +-- coderd/httpmw/workspacebuildparam.go | 3 +- coderd/httpmw/workspaceparam.go | 3 +- coderd/httpmw/workspaceresourceparam.go | 11 +-- coderd/members.go | 4 +- coderd/organizations.go | 6 +- coderd/organizations_test.go | 6 +- coderd/pagination.go | 2 +- coderd/pagination_internal_test.go | 2 +- coderd/parameters.go | 26 +++--- coderd/parameters_test.go | 6 +- coderd/provisionerdaemons.go | 3 +- coderd/provisionerjobs.go | 28 +++---- coderd/roles.go | 2 +- coderd/roles_test.go | 2 +- coderd/templates.go | 52 ++++++------ coderd/templates_test.go | 12 +-- coderd/templateversions.go | 90 ++++++++++----------- coderd/templateversions_test.go | 26 +++--- coderd/userauth.go | 22 +++--- coderd/users.go | 98 +++++++++++------------ coderd/users_test.go | 24 +++--- coderd/workspaceagents.go | 56 ++++++------- coderd/workspaceapps.go | 21 ++--- coderd/workspacebuilds.go | 84 ++++++++++---------- coderd/workspacebuilds_test.go | 10 +-- coderd/workspaceresourceauth.go | 26 +++--- coderd/workspaceresourceauth_test.go | 4 +- coderd/workspaceresources.go | 10 +-- coderd/workspaces.go | 100 ++++++++++++------------ coderd/workspaces_test.go | 30 +++---- codersdk/client.go | 30 +++---- codersdk/error.go | 27 +++++++ codersdk/provisionerdaemons.go | 4 +- codersdk/workspaceagents.go | 9 +-- 54 files changed, 526 insertions(+), 507 deletions(-) create mode 100644 codersdk/error.go diff --git a/cli/userlist_test.go b/cli/userlist_test.go index c8b207a0a92fb..fa5a15c79f370 100644 --- a/cli/userlist_test.go +++ b/cli/userlist_test.go @@ -49,7 +49,7 @@ func TestUserList(t *testing.T) { _, err := cmd.ExecuteC() - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Contains(t, err.Error(), "Try logging in using 'coder login '.") }) diff --git a/coderd/coderd.go b/coderd/coderd.go index a8cdf847fc4c7..4572beed5057f 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -134,7 +134,7 @@ func New(options *Options) *API { r.Route("/api/v2", func(r chi.Router) { r.NotFound(func(rw http.ResponseWriter, r *http.Request) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Route not found.", }) }) @@ -144,7 +144,7 @@ func New(options *Options) *API { debugLogRequest(api.Logger), ) r.Get("/", func(w http.ResponseWriter, r *http.Request) { - httpapi.Write(w, http.StatusOK, httpapi.Response{ + httpapi.Write(w, http.StatusOK, codersdk.Response{ //nolint:gocritic Message: "👋", }) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index a6ace8eb20c38..28582c62c17d5 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -278,7 +278,7 @@ func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationI } user, err := client.CreateUser(context.Background(), req) - var apiError *codersdk.Error + var apiError *codersdk.HTTPError // If the user already exists by username or email conflict, try again up to "retries" times. if err != nil && retries >= 0 && xerrors.As(err, &apiError) { if apiError.StatusCode() == http.StatusConflict { diff --git a/coderd/csp.go b/coderd/csp.go index c1f89ce061aae..f815bc48bd665 100644 --- a/coderd/csp.go +++ b/coderd/csp.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" "cdr.dev/slog" ) @@ -22,7 +23,7 @@ func (api *API) logReportCSPViolations(rw http.ResponseWriter, r *http.Request) err := dec.Decode(&v) if err != nil { api.Logger.Warn(ctx, "csp violation", slog.Error(err)) - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to read body, invalid json.", Detail: err.Error(), }) diff --git a/coderd/files.go b/coderd/files.go index cca2cb391ef5a..7053e6d7c10fa 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -32,7 +32,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { switch contentType { case "application/x-tar": default: - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Unsupported content type header %q.", contentType), }) return @@ -41,7 +41,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(rw, r.Body, 10*(10<<20)) data, err := io.ReadAll(r.Body) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to read file from request.", Detail: err.Error(), }) @@ -65,7 +65,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { Data: data, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error saving file.", Detail: err.Error(), }) @@ -80,7 +80,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) { hash := chi.URLParam(r, "hash") if hash == "" { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "File hash must be provided in url.", }) return @@ -91,7 +91,7 @@ func (api *API) fileByHash(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching file.", Detail: err.Error(), }) diff --git a/coderd/files_test.go b/coderd/files_test.go index 016774a030c88..57beac26c0c1a 100644 --- a/coderd/files_test.go +++ b/coderd/files_test.go @@ -48,7 +48,7 @@ func TestDownload(t *testing.T) { client := coderdtest.New(t, nil) _ = coderdtest.CreateFirstUser(t, client) _, _, err := client.Download(context.Background(), "something") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 713dfe9e1bc49..d4b035e654495 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -21,7 +21,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error generating a new SSH keypair.", Detail: err.Error(), }) @@ -35,7 +35,7 @@ func (api *API) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { PublicKey: publicKey, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating user's git SSH key.", Detail: err.Error(), }) @@ -44,7 +44,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's git SSH key.", Detail: err.Error(), }) @@ -70,7 +70,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's SSH key.", Detail: err.Error(), }) @@ -90,7 +90,7 @@ func (api *API) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { agent := httpmw.WorkspaceAgent(r) resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace resource.", Detail: err.Error(), }) @@ -99,7 +99,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -108,7 +108,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace.", Detail: err.Error(), }) @@ -117,7 +117,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching git SSH key.", Detail: err.Error(), }) diff --git a/coderd/httpapi/httpapi.go b/coderd/httpapi/httpapi.go index 79e36f175b26b..f16840b958fba 100644 --- a/coderd/httpapi/httpapi.go +++ b/coderd/httpapi/httpapi.go @@ -11,6 +11,8 @@ import ( "strings" "github.com/go-playground/validator/v10" + + "github.com/coder/coder/codersdk" ) var ( @@ -50,42 +52,16 @@ func init() { } } -// Response represents a generic HTTP response. -type Response struct { - // 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,omitempty"` - // 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:"validations,omitempty"` -} - -// Error represents a scoped error to a user input. -type Error struct { - Field string `json:"field" validate:"required"` - Detail string `json:"detail" validate:"required"` -} - // ResourceNotFound is intentionally vague. All 404 responses should be identical // to prevent leaking existence of resources. func ResourceNotFound(rw http.ResponseWriter) { - Write(rw, http.StatusNotFound, Response{ + Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Resource not found or you do not have access to this resource", }) } func Forbidden(rw http.ResponseWriter) { - Write(rw, http.StatusForbidden, Response{ + Write(rw, http.StatusForbidden, codersdk.Response{ Message: "Forbidden.", }) } @@ -114,7 +90,7 @@ func Write(rw http.ResponseWriter, status int, response interface{}) { 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{ + Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Request body must be valid JSON.", Detail: err.Error(), }) @@ -123,21 +99,21 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool { err = validate.Struct(value) var validationErrors validator.ValidationErrors if errors.As(err, &validationErrors) { - apiErrors := make([]Error, 0, len(validationErrors)) + apiErrors := make([]codersdk.Error, 0, len(validationErrors)) for _, validationError := range validationErrors { - apiErrors = append(apiErrors, Error{ + apiErrors = append(apiErrors, codersdk.Error{ Field: validationError.Field(), Detail: fmt.Sprintf("Validation failed for tag %q with value: \"%v\"", validationError.Tag(), validationError.Value()), }) } - Write(rw, http.StatusBadRequest, Response{ + Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Validation failed.", Validations: apiErrors, }) return false } if err != nil { - Write(rw, http.StatusInternalServerError, Response{ + Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error validating request body payload.", Detail: err.Error(), }) diff --git a/coderd/httpapi/queryparams.go b/coderd/httpapi/queryparams.go index 9873a9a059aea..a5f66b1f639bc 100644 --- a/coderd/httpapi/queryparams.go +++ b/coderd/httpapi/queryparams.go @@ -8,6 +8,8 @@ import ( "github.com/google/uuid" + "github.com/coder/coder/codersdk" + "golang.org/x/xerrors" ) @@ -17,19 +19,19 @@ import ( type QueryParamParser struct { // Errors is the set of errors to return via the API. If the length // of this set is 0, there are no errors!. - Errors []Error + Errors []codersdk.Error } func NewQueryParamParser() *QueryParamParser { return &QueryParamParser{ - Errors: []Error{}, + Errors: []codersdk.Error{}, } } func (p *QueryParamParser) Int(vals url.Values, def int, queryParam string) int { v, err := parseQueryParam(vals, strconv.Atoi, def, queryParam) if err != nil { - p.Errors = append(p.Errors, Error{ + p.Errors = append(p.Errors, codersdk.Error{ Field: queryParam, Detail: fmt.Sprintf("Query param %q must be a valid integer (%s)", queryParam, err.Error()), }) @@ -47,7 +49,7 @@ func (p *QueryParamParser) UUIDorMe(vals url.Values, def uuid.UUID, me uuid.UUID func (p *QueryParamParser) UUID(vals url.Values, def uuid.UUID, queryParam string) uuid.UUID { v, err := parseQueryParam(vals, uuid.Parse, def, queryParam) if err != nil { - p.Errors = append(p.Errors, Error{ + p.Errors = append(p.Errors, codersdk.Error{ Field: queryParam, Detail: fmt.Sprintf("Query param %q must be a valid uuid", queryParam), }) @@ -75,7 +77,7 @@ func (p *QueryParamParser) UUIDs(vals url.Values, def []uuid.UUID, queryParam st return ids, nil }, def, queryParam) if err != nil { - p.Errors = append(p.Errors, Error{ + p.Errors = append(p.Errors, codersdk.Error{ Field: queryParam, Detail: fmt.Sprintf("Query param %q has invalid uuids: %q", queryParam, err.Error()), }) @@ -105,7 +107,7 @@ func (*QueryParamParser) Strings(vals url.Values, def []string, queryParam strin func ParseCustom[T any](parser *QueryParamParser, vals url.Values, def T, queryParam string, parseFunc func(v string) (T, error)) T { v, err := parseQueryParam(vals, parseFunc, def, queryParam) if err != nil { - parser.Errors = append(parser.Errors, Error{ + parser.Errors = append(parser.Errors, codersdk.Error{ Field: queryParam, Detail: fmt.Sprintf("Query param %q has invalid value: %s", queryParam, err.Error()), }) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 596dce68fea56..364ebeadd2681 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -18,6 +18,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) // SessionTokenKey represents the name of the cookie or query parameter the API key is stored in. @@ -63,7 +64,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Write wraps writing a response to redirect if the handler // specified it should. This redirect is used for user-facing // pages like workspace applications. - write := func(code int, response httpapi.Response) { + write := func(code int, response codersdk.Response) { if redirectToLogin { q := r.URL.Query() q.Add("message", response.Message) @@ -84,7 +85,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool cookieValue = cookie.Value } if cookieValue == "" { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Cookie %q or query parameter must be provided.", SessionTokenKey), }) return @@ -92,7 +93,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool parts := strings.Split(cookieValue, "-") // APIKeys are formatted: ID-SECRET if len(parts) != 2 { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Invalid %q cookie API key format.", SessionTokenKey), }) return @@ -101,13 +102,13 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool keySecret := parts[1] // Ensuring key lengths are valid. if len(keyID) != 10 { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Invalid %q cookie API key id.", SessionTokenKey), }) return } if len(keySecret) != 22 { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Invalid %q cookie API key secret.", SessionTokenKey), }) return @@ -115,12 +116,12 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool key, err := db.GetAPIKeyByID(r.Context(), keyID) if err != nil { if errors.Is(err, sql.ErrNoRows) { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: "API key is invalid.", }) return } - write(http.StatusInternalServerError, httpapi.Response{ + write(http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching API key by id.", Detail: err.Error(), }) @@ -130,7 +131,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking to see if the secret is valid. if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: "API key secret is invalid.", }) return @@ -147,7 +148,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool case database.LoginTypeGithub: oauthConfig = oauth.Github default: - write(http.StatusInternalServerError, httpapi.Response{ + write(http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Unexpected authentication type %q.", key.LoginType), }) return @@ -159,7 +160,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool Expiry: key.OAuthExpiry, }).Token() if err != nil { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: "Could not refresh expired Oauth token.", Detail: err.Error(), }) @@ -175,7 +176,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Checking if the key is expired. if key.ExpiresAt.Before(now) { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("API key expired at %q.", key.ExpiresAt.String()), }) return @@ -217,7 +218,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool OAuthExpiry: key.OAuthExpiry, }) if err != nil { - write(http.StatusInternalServerError, httpapi.Response{ + write(http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("API key couldn't update: %s.", err.Error()), }) return @@ -229,7 +230,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // is to block 'suspended' users from accessing the platform. roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID) if err != nil { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: "Internal error fetching user's roles.", Detail: err.Error(), }) @@ -237,7 +238,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool } if roles.Status != database.UserStatusActive { - write(http.StatusUnauthorized, httpapi.Response{ + write(http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("User is not active (status = %q). Contact an admin to reactivate your account.", roles.Status), }) return diff --git a/coderd/httpmw/apikey_test.go b/coderd/httpmw/apikey_test.go index bd3c1aa27f818..5fa21fd9cd7b6 100644 --- a/coderd/httpmw/apikey_test.go +++ b/coderd/httpmw/apikey_test.go @@ -18,6 +18,7 @@ import ( "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -32,7 +33,7 @@ func TestAPIKey(t *testing.T) { successHandler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // Only called if the API key passes through the handler. - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "It worked!", }) }) @@ -219,7 +220,7 @@ func TestAPIKey(t *testing.T) { httpmw.ExtractAPIKey(db, nil, false)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // Checks that it exists on the context! _ = httpmw.APIKey(r) - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "It worked!", }) })).ServeHTTP(rw, r) @@ -257,7 +258,7 @@ func TestAPIKey(t *testing.T) { httpmw.ExtractAPIKey(db, nil, false)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // Checks that it exists on the context! _ = httpmw.APIKey(r) - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "It worked!", }) })).ServeHTTP(rw, r) diff --git a/coderd/httpmw/httpmw.go b/coderd/httpmw/httpmw.go index 601b316ce389c..1dde571b875a3 100644 --- a/coderd/httpmw/httpmw.go +++ b/coderd/httpmw/httpmw.go @@ -8,13 +8,14 @@ import ( "github.com/google/uuid" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) // parseUUID consumes a url parameter and parses it as a UUID. func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID, bool) { rawID := chi.URLParam(r, param) if rawID == "" { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Missing UUID in URL.", // Url params mean nothing to a user Detail: fmt.Sprintf("%q URL param missing", param), @@ -24,7 +25,7 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid UUID %q.", param), Detail: err.Error(), }) diff --git a/coderd/httpmw/oauth2.go b/coderd/httpmw/oauth2.go index e880f8163d6d9..f9c44ecbc69bd 100644 --- a/coderd/httpmw/oauth2.go +++ b/coderd/httpmw/oauth2.go @@ -9,6 +9,7 @@ import ( "golang.org/x/oauth2" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -49,7 +50,7 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // Interfaces can hold a nil value if config == nil || reflect.ValueOf(config).IsNil() { - httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionRequired, codersdk.Response{ Message: "The oauth2 method requested is not configured!", }) return @@ -62,7 +63,7 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler { // If the code isn't provided, we'll redirect! state, err := cryptorand.String(32) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error generating state string.", Detail: err.Error(), }) @@ -91,7 +92,7 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler { } if state == "" { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Cookie %q must be provided.", oauth2StateCookieName), }) return } if stateCookie.Value != state { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "State mismatched.", }) return @@ -119,7 +120,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error exchanging Oauth code.", Detail: err.Error(), }) diff --git a/coderd/httpmw/organizationparam.go b/coderd/httpmw/organizationparam.go index 83ef445a9d926..f3baacd42b5f6 100644 --- a/coderd/httpmw/organizationparam.go +++ b/coderd/httpmw/organizationparam.go @@ -8,6 +8,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type organizationParamContextKey struct{} @@ -48,7 +49,7 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching organization.", Detail: err.Error(), }) @@ -77,7 +78,7 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching organization member.", Detail: err.Error(), }) diff --git a/coderd/httpmw/ratelimit.go b/coderd/httpmw/ratelimit.go index c5ddc978d0998..366e110eb4ee9 100644 --- a/coderd/httpmw/ratelimit.go +++ b/coderd/httpmw/ratelimit.go @@ -8,6 +8,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) // RateLimitPerMinute returns a handler that limits requests per-minute based @@ -31,7 +32,7 @@ func RateLimitPerMinute(count int) func(http.Handler) http.Handler { return httprate.KeyByIP(r) }, httprate.KeyByEndpoint), httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) { - httpapi.Write(w, http.StatusTooManyRequests, httpapi.Response{ + httpapi.Write(w, http.StatusTooManyRequests, codersdk.Response{ Message: "You've been rate limited for sending too many requests!", }) }), diff --git a/coderd/httpmw/templateparam.go b/coderd/httpmw/templateparam.go index 9261a346bb40f..cc9cb0b542337 100644 --- a/coderd/httpmw/templateparam.go +++ b/coderd/httpmw/templateparam.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type templateParamContextKey struct{} @@ -37,7 +38,7 @@ func ExtractTemplateParam(db database.Store) func(http.Handler) http.Handler { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) diff --git a/coderd/httpmw/templateversionparam.go b/coderd/httpmw/templateversionparam.go index dc85383dd48e8..762f5dae44440 100644 --- a/coderd/httpmw/templateversionparam.go +++ b/coderd/httpmw/templateversionparam.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type templateVersionParamContextKey struct{} @@ -37,7 +38,7 @@ func ExtractTemplateVersionParam(db database.Store) func(http.Handler) http.Hand return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) diff --git a/coderd/httpmw/userparam.go b/coderd/httpmw/userparam.go index 924f3362b9ec1..7f16ac82c91cc 100644 --- a/coderd/httpmw/userparam.go +++ b/coderd/httpmw/userparam.go @@ -12,6 +12,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type userParamContextKey struct{} @@ -42,7 +43,7 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler { // userQuery is either a uuid, a username, or 'me' userQuery := chi.URLParam(r, "user") if userQuery == "" { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "\"user\" must be provided.", }) return @@ -55,7 +56,7 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -65,7 +66,7 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler { // If the userQuery is a valid uuid user, err = db.GetUserByID(r.Context(), userID) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: userErrorMessage, }) return @@ -76,7 +77,7 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler { Username: userQuery, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: userErrorMessage, }) return diff --git a/coderd/httpmw/workspaceagent.go b/coderd/httpmw/workspaceagent.go index 3de760b2d0291..317c7b858e552 100644 --- a/coderd/httpmw/workspaceagent.go +++ b/coderd/httpmw/workspaceagent.go @@ -11,6 +11,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type workspaceAgentContextKey struct{} @@ -30,14 +31,14 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(SessionTokenKey) if err != nil { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Cookie %q must be provided.", SessionTokenKey), }) return } token, err := uuid.Parse(cookie.Value) if err != nil { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Agent token is invalid.", }) return @@ -45,13 +46,13 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler { agent, err := db.GetWorkspaceAgentByAuthToken(r.Context(), token) if err != nil { if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Agent token is invalid.", }) return } - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agent.", Detail: err.Error(), }) diff --git a/coderd/httpmw/workspaceagentparam.go b/coderd/httpmw/workspaceagentparam.go index 033a7410a1845..0f10ea76780dc 100644 --- a/coderd/httpmw/workspaceagentparam.go +++ b/coderd/httpmw/workspaceagentparam.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type workspaceAgentParamContextKey struct{} @@ -34,13 +35,13 @@ 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{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Agent doesn't exist with that id.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agent.", Detail: err.Error(), }) @@ -49,7 +50,7 @@ func ExtractWorkspaceAgentParam(db database.Store) func(http.Handler) http.Handl resource, err := db.GetWorkspaceResourceByID(r.Context(), agent.ResourceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace resource.", Detail: err.Error(), }) @@ -58,21 +59,21 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if job.Type != database.ProvisionerJobTypeWorkspaceBuild { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Workspace agents can only be fetched for builds.", }) return } build, err := db.GetWorkspaceBuildByJobID(r.Context(), job.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) diff --git a/coderd/httpmw/workspacebuildparam.go b/coderd/httpmw/workspacebuildparam.go index 33375b300e921..307252bdf619b 100644 --- a/coderd/httpmw/workspacebuildparam.go +++ b/coderd/httpmw/workspacebuildparam.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type workspaceBuildParamContextKey struct{} @@ -37,7 +38,7 @@ func ExtractWorkspaceBuildParam(db database.Store) func(http.Handler) http.Handl return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) diff --git a/coderd/httpmw/workspaceparam.go b/coderd/httpmw/workspaceparam.go index ce36a73ff85f8..cd0fa8625813c 100644 --- a/coderd/httpmw/workspaceparam.go +++ b/coderd/httpmw/workspaceparam.go @@ -8,6 +8,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type workspaceParamContextKey struct{} @@ -35,7 +36,7 @@ func ExtractWorkspaceParam(db database.Store) func(http.Handler) http.Handler { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace.", Detail: err.Error(), }) diff --git a/coderd/httpmw/workspaceresourceparam.go b/coderd/httpmw/workspaceresourceparam.go index 5b2f9652fa55c..fa30d327efef9 100644 --- a/coderd/httpmw/workspaceresourceparam.go +++ b/coderd/httpmw/workspaceresourceparam.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) type workspaceResourceParamContextKey struct{} @@ -33,13 +34,13 @@ 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{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Resource doesn't exist with that id.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner resource.", Detail: err.Error(), }) @@ -48,21 +49,21 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error provisioner job.", Detail: err.Error(), }) return } if job.Type != database.ProvisionerJobTypeWorkspaceBuild { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Workspace resources can only be fetched for builds.", }) return } build, err := db.GetWorkspaceBuildByJobID(r.Context(), job.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error workspace build.", Detail: err.Error(), }) diff --git a/coderd/members.go b/coderd/members.go index 22c2d8804f873..f0f4e4dbbbc07 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -23,7 +23,7 @@ func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) { apiKey := httpmw.APIKey(r) if apiKey.UserID == member.UserID { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "You cannot change your own organization roles.", }) return @@ -58,7 +58,7 @@ func (api *API) putMemberRoles(rw http.ResponseWriter, r *http.Request) { OrgID: organization.ID, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: err.Error(), }) return diff --git a/coderd/organizations.go b/coderd/organizations.go index 30aeac30493cc..3755777692e75 100644 --- a/coderd/organizations.go +++ b/coderd/organizations.go @@ -45,13 +45,13 @@ 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{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: "Organization already exists with that name.", }) return } if !errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Internal error fetching organization %q.", req.Name), Detail: err.Error(), }) @@ -84,7 +84,7 @@ func (api *API) postOrganizations(rw http.ResponseWriter, r *http.Request) { return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting organization member.", Detail: err.Error(), }) diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index d329c65d59832..0e6fa7871755d 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -28,7 +28,7 @@ func TestOrganizationByUserAndName(t *testing.T) { client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) _, err := client.OrganizationByName(context.Background(), codersdk.Me, "nothing") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -43,7 +43,7 @@ func TestOrganizationByUserAndName(t *testing.T) { }) require.NoError(t, err) _, err = other.OrganizationByName(context.Background(), codersdk.Me, org.Name) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -70,7 +70,7 @@ func TestPostOrganizationsByUser(t *testing.T) { _, err = client.CreateOrganization(context.Background(), codersdk.CreateOrganizationRequest{ Name: org.Name, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) diff --git a/coderd/pagination.go b/coderd/pagination.go index 25d1465ef5092..918bc0d5fefc0 100644 --- a/coderd/pagination.go +++ b/coderd/pagination.go @@ -21,7 +21,7 @@ func parsePagination(w http.ResponseWriter, r *http.Request) (p codersdk.Paginat Offset: parser.Int(queryParams, 0, "offset"), } if len(parser.Errors) > 0 { - httpapi.Write(w, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(w, http.StatusBadRequest, codersdk.Response{ Message: "Query parameters have invalid values.", Validations: parser.Errors, }) diff --git a/coderd/pagination_internal_test.go b/coderd/pagination_internal_test.go index 978cfab417b2e..c37de9f6c54bd 100644 --- a/coderd/pagination_internal_test.go +++ b/coderd/pagination_internal_test.go @@ -110,7 +110,7 @@ func TestPagination(t *testing.T) { } else { require.False(t, ok, "expect !ok") require.Equal(t, http.StatusBadRequest, rw.Code, "bad request status code") - var apiError codersdk.Error + var apiError codersdk.HTTPError err := json.NewDecoder(rw.Body).Decode(&apiError) require.NoError(t, err, "decode response") require.Contains(t, apiError.Message, c.ExpectedError, "expected error") diff --git a/coderd/parameters.go b/coderd/parameters.go index 1d22d267fbe2b..016ce9fb7ee8d 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -41,13 +41,13 @@ func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) { Name: createRequest.Name, }) if err == nil { - httpapi.Write(rw, http.StatusConflict, httpapi.Response{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching parameter.", Detail: err.Error(), }) @@ -66,7 +66,7 @@ func (api *API) postParameter(rw http.ResponseWriter, r *http.Request) { DestinationScheme: database.ParameterDestinationScheme(createRequest.DestinationScheme), }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting parameter.", Detail: err.Error(), }) @@ -99,7 +99,7 @@ func (api *API) parameters(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching parameter scope values.", Detail: err.Error(), }) @@ -139,7 +139,7 @@ func (api *API) deleteParameter(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching parameter.", Detail: err.Error(), }) @@ -147,13 +147,13 @@ func (api *API) deleteParameter(rw http.ResponseWriter, r *http.Request) { } err = api.Database.DeleteParameterValueByID(r.Context(), parameterValue.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error deleting parameter.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Parameter deleted.", }) } @@ -225,11 +225,11 @@ func (api *API) parameterRBACResource(rw http.ResponseWriter, r *http.Request, s // Write error payload to rw if we cannot find the resource for the scope if err != nil { if xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Scope %q resource %q not found.", scope, scopeID), }) } else { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: err.Error(), }) } @@ -243,9 +243,9 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter switch scope { case database.ParameterScopeTemplate, database.ParameterScopeImportJob, database.ParameterScopeWorkspace: default: - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid scope %q.", scope), - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "scope", Detail: "invalid scope"}, }, }) @@ -255,10 +255,10 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter id := chi.URLParam(r, "id") uid, err := uuid.Parse(id) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid UUID %q.", id), Detail: err.Error(), - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "id", Detail: "Invalid UUID"}, }, }) diff --git a/coderd/parameters_test.go b/coderd/parameters_test.go index b0e519b938847..b05084178c1d4 100644 --- a/coderd/parameters_test.go +++ b/coderd/parameters_test.go @@ -26,7 +26,7 @@ func TestPostParameter(t *testing.T) { SourceScheme: codersdk.ParameterSourceSchemeData, DestinationScheme: codersdk.ParameterDestinationSchemeProvisionerVariable, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -64,7 +64,7 @@ func TestPostParameter(t *testing.T) { SourceScheme: codersdk.ParameterSourceSchemeData, DestinationScheme: codersdk.ParameterDestinationSchemeProvisionerVariable, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -106,7 +106,7 @@ func TestDeleteParameter(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) template := createTemplate(t, client, user) err := client.DeleteParameter(context.Background(), codersdk.ParameterTemplate, template.ID, "something") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 3e09ed02a5fec..3fb23b4e5acd2 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -28,6 +28,7 @@ import ( "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/coderd/rbac" "github.com/coder/coder/coderd/telemetry" + "github.com/coder/coder/codersdk" "github.com/coder/coder/peer/peerwg" "github.com/coder/coder/provisionerd/proto" "github.com/coder/coder/provisionersdk" @@ -40,7 +41,7 @@ func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner daemons.", Detail: err.Error(), }) diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 97aafc95909d1..bc435a890e81c 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -33,7 +33,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job afterRaw := r.URL.Query().Get("after") beforeRaw := r.URL.Query().Get("before") if beforeRaw != "" && follow { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param \"before\" cannot be used with \"follow\".", }) return @@ -46,7 +46,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job if follow { bl, closeFollow, err := api.followLogs(job.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error watching provisioner logs.", Detail: err.Error(), }) @@ -62,7 +62,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job // avoid this, but not worth it for one fewer query at this point. job, err = api.Database.GetProvisionerJobByID(r.Context(), job.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error querying job.", Detail: err.Error(), }) @@ -75,9 +75,9 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job if afterRaw != "" { afterMS, err := strconv.ParseInt(afterRaw, 10, 64) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param \"after\" must be an integer.", - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "after", Detail: "Must be an integer"}, }, }) @@ -94,9 +94,9 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job if beforeRaw != "" { beforeMS, err := strconv.ParseInt(beforeRaw, 10, 64) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param \"before\" must be an integer.", - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "before", Detail: "Must be an integer"}, }, }) @@ -119,7 +119,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner logs.", Detail: err.Error(), }) @@ -141,7 +141,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job defer api.websocketWaitGroup.Done() conn, err := websocket.Accept(rw, r, nil) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -195,7 +195,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, job database.ProvisionerJob) { if !job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job hasn't completed!", }) return @@ -205,7 +205,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching job resources.", Detail: err.Error(), }) @@ -220,7 +220,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agent.", Detail: err.Error(), }) @@ -235,7 +235,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace applications.", Detail: err.Error(), }) @@ -258,7 +258,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, apiAgent, err := convertWorkspaceAgent(agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading job agent.", Detail: err.Error(), }) diff --git a/coderd/roles.go b/coderd/roles.go index 30f78275e0a1d..36d5d88d734dc 100644 --- a/coderd/roles.go +++ b/coderd/roles.go @@ -63,7 +63,7 @@ func (api *API) checkPermissions(rw http.ResponseWriter, r *http.Request) { response := make(codersdk.UserAuthorizationResponse) for k, v := range params.Checks { if v.Object.ResourceType == "" { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Object's \"resource_type\" field must be defined for key %q.", k), }) return diff --git a/coderd/roles_test.go b/coderd/roles_test.go index a30daa46ba7a7..d219dd6f4966e 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -190,7 +190,7 @@ func TestListRoles(t *testing.T) { t.Parallel() roles, err := c.APICall() if c.AuthorizedError != "" { - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusForbidden, apiErr.StatusCode()) require.Contains(t, apiErr.Message, c.AuthorizedError) diff --git a/coderd/templates.go b/coderd/templates.go index 1cf87c86bbefb..d69f27035dff9 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -40,7 +40,7 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace count.", Detail: err.Error(), }) @@ -59,7 +59,7 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) { createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{template}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching creator name.", Detail: err.Error(), }) @@ -83,14 +83,14 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspaces by template id.", Detail: err.Error(), }) return } if len(workspaces) > 0 { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "All workspaces must be deleted before a template can be removed.", }) return @@ -101,13 +101,13 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) { UpdatedAt: database.Now(), }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error deleting template.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Template has been deleted!", }) } @@ -130,9 +130,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque Name: createTemplate.Name, }) if err == nil { - httpapi.Write(rw, http.StatusConflict, httpapi.Response{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: fmt.Sprintf("Template with name %q already exists.", createTemplate.Name), - Validations: []httpapi.Error{{ + Validations: []codersdk.Error{{ Field: "name", Detail: "This value is already in use and should be unique.", }}, @@ -140,7 +140,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } if !errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template by name.", Detail: err.Error(), }) @@ -148,16 +148,16 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } templateVersion, err := api.Database.GetTemplateVersionByID(r.Context(), createTemplate.VersionID) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Template version %q does not exist.", createTemplate.VersionID), - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "template_version_id", Detail: "Template version does not exist"}, }, }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) @@ -165,7 +165,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque } importJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -240,7 +240,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting template.", Detail: err.Error(), }) @@ -264,7 +264,7 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching templates in organization.", Detail: err.Error(), }) @@ -284,7 +284,7 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace counts.", Detail: err.Error(), }) @@ -293,7 +293,7 @@ func (api *API) templatesByOrganization(rw http.ResponseWriter, r *http.Request) createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, templates) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching creator names.", Detail: err.Error(), }) @@ -316,7 +316,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re return } - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) @@ -333,7 +333,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace counts.", Detail: err.Error(), }) @@ -347,7 +347,7 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{template}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching creator name.", Detail: err.Error(), }) @@ -369,16 +369,16 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return } - var validErrs []httpapi.Error + var validErrs []codersdk.Error if req.MaxTTLMillis < 0 { - validErrs = append(validErrs, httpapi.Error{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) + validErrs = append(validErrs, codersdk.Error{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) } if req.MinAutostartIntervalMillis < 0 { - validErrs = append(validErrs, httpapi.Error{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."}) + validErrs = append(validErrs, codersdk.Error{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."}) } if len(validErrs) > 0 { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid request to update template metadata!", Validations: validErrs, }) @@ -439,7 +439,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating template metadata.", Detail: err.Error(), }) @@ -453,7 +453,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { createdByNameMap, err := getCreatedByNamesByTemplateIDs(r.Context(), api.Database, []database.Template{updated}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching creator name.", Detail: err.Error(), }) diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 9f54d59003e9d..60b9a87fbdacf 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -88,7 +88,7 @@ func TestPostTemplateByOrganization(t *testing.T) { Name: template.Name, VersionID: version.ID, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -101,7 +101,7 @@ func TestPostTemplateByOrganization(t *testing.T) { VersionID: uuid.New(), }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) require.Contains(t, err.Error(), "Try logging in using 'coder login '.") @@ -115,7 +115,7 @@ func TestPostTemplateByOrganization(t *testing.T) { Name: "test", VersionID: uuid.New(), }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -163,7 +163,7 @@ func TestTemplateByOrganizationAndName(t *testing.T) { client := coderdtest.New(t, nil) user := coderdtest.CreateFirstUser(t, client) _, err := client.TemplateByName(context.Background(), user.OrganizationID, "something") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -259,7 +259,7 @@ func TestPatchTemplateMeta(t *testing.T) { MinAutostartIntervalMillis: -int64(time.Hour), } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Contains(t, apiErr.Message, "Invalid request") require.Len(t, apiErr.Validations, 2) @@ -297,7 +297,7 @@ func TestDeleteTemplate(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) err := client.DeleteTemplate(context.Background(), template.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/templateversions.go b/coderd/templateversions.go index ccaea9c005f7f..5b385fca946e5 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -29,7 +29,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -48,20 +48,20 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already completed!", }) return } if job.CanceledAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already been marked as canceled!", }) return @@ -74,13 +74,13 @@ func (api *API) patchCancelTemplateVersion(rw http.ResponseWriter, r *http.Reque }, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating provisioner job.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Job has been marked as canceled...", }) } @@ -94,14 +94,14 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if !job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Template version job hasn't completed!", }) return @@ -111,7 +111,7 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error listing parameter schemas.", Detail: err.Error(), }) @@ -121,7 +121,7 @@ func (api *API) templateVersionSchema(rw http.ResponseWriter, r *http.Request) { for _, schema := range schemas { apiSchema, err := convertParameterSchema(schema) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Internal error converting schema %s.", schema.Name), Detail: err.Error(), }) @@ -142,14 +142,14 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if !job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job hasn't completed!", }) return @@ -163,7 +163,7 @@ func (api *API) templateVersionParameters(rw http.ResponseWriter, r *http.Reques HideRedisplayValues: true, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error computing values.", Detail: err.Error(), }) @@ -198,14 +198,14 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating provisioner job.", Detail: err.Error(), }) return } if !job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Template version import job hasn't completed!", }) return @@ -233,7 +233,7 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques ParameterValues: parameterValues, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error unmarshalling provisioner job.", Detail: err.Error(), }) @@ -255,7 +255,7 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques Input: input, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting provisioner job.", Detail: err.Error(), }) @@ -306,13 +306,13 @@ func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http } if job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already completed.", }) return } if job.CanceledAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already been marked as canceled.", }) return @@ -326,14 +326,14 @@ func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http }, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating provisioner job.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Job has been marked as canceled.", }) } @@ -350,7 +350,7 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Job ID %q must be a valid UUID.", jobID), Detail: err.Error(), }) @@ -359,13 +359,13 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re job, err := api.Database.GetProvisionerJobByID(r.Context(), jobUUID) if xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Provisioner job %q not found.", jobUUID), }) return database.ProvisionerJob{}, false } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -386,7 +386,7 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re var input templateVersionDryRunJob err = json.Unmarshal(job.Input, &input) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error unmarshaling job metadata.", Detail: err.Error(), }) @@ -420,12 +420,12 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque // query will not work. _, err := store.GetTemplateVersionByID(r.Context(), paginationParams.AfterID) if err != nil && xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version at after_id.", Detail: err.Error(), }) @@ -444,7 +444,7 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque return err } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template versions.", Detail: err.Error(), }) @@ -457,7 +457,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -471,7 +471,7 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque for _, version := range versions { job, exists := jobByID[version.JobID.String()] if !exists { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Job %q doesn't exist for version %q.", version.JobID, version.ID), }) return err @@ -504,13 +504,13 @@ func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) { Name: templateVersionName, }) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("No template version found by name %q.", templateVersionName), }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) @@ -518,7 +518,7 @@ func (api *API) templateVersionByName(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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -541,20 +541,20 @@ 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{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Template version not found.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) return } if version.TemplateID.UUID.String() != template.ID.String() { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "The provided template version doesn't belong to the specified template.", }) return @@ -572,13 +572,13 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating active template version.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Updated the active template version!", }) } @@ -595,13 +595,13 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht if req.TemplateID != uuid.Nil { _, err := api.Database.GetTemplateByID(r.Context(), req.TemplateID) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Template does not exist.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) @@ -611,13 +611,13 @@ 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{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "File not found.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching file.", Detail: err.Error(), }) @@ -742,7 +742,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: err.Error(), }) return @@ -765,7 +765,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -787,7 +787,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index a2cc37fc49931..d2e5759711354 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -41,7 +41,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { StorageSource: "hash", Provisioner: codersdk.ProvisionerTypeEcho, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -55,7 +55,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { StorageSource: "hash", Provisioner: codersdk.ProvisionerTypeEcho, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -96,7 +96,7 @@ func TestPatchCancelTemplateVersion(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) err := client.CancelTemplateVersion(context.Background(), version.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -122,7 +122,7 @@ func TestPatchCancelTemplateVersion(t *testing.T) { err := client.CancelTemplateVersion(context.Background(), version.ID) require.NoError(t, err) err = client.CancelTemplateVersion(context.Background(), version.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -164,7 +164,7 @@ func TestTemplateVersionSchema(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionSchema(context.Background(), version.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -232,7 +232,7 @@ func TestTemplateVersionParameters(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionParameters(context.Background(), version.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -277,7 +277,7 @@ func TestTemplateVersionResources(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionResources(context.Background(), version.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -388,7 +388,7 @@ func TestTemplateVersionByName(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) _, err := client.TemplateVersionByName(context.Background(), template.ID, "nothing") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -415,7 +415,7 @@ func TestPatchActiveTemplateVersion(t *testing.T) { err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: uuid.New(), }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -430,7 +430,7 @@ func TestPatchActiveTemplateVersion(t *testing.T) { err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: version.ID, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -543,7 +543,7 @@ func TestTemplateVersionDryRun(t *testing.T) { _, err := client.CreateTemplateVersionDryRun(context.Background(), version.ID, codersdk.CreateTemplateVersionDryRunRequest{ ParameterValues: []codersdk.CreateParameterRequest{}, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -625,7 +625,7 @@ func TestTemplateVersionDryRun(t *testing.T) { }, 5*time.Second, 25*time.Millisecond) err = client.CancelTemplateVersionDryRun(context.Background(), version.ID, job.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -666,7 +666,7 @@ func TestTemplateVersionDryRun(t *testing.T) { require.NoError(t, err) err = client.CancelTemplateVersionDryRun(context.Background(), version.ID, job.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/userauth.go b/coderd/userauth.go index afc759321e1db..a9d23408bd0ad 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -49,7 +49,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { oauthClient := oauth2.NewClient(r.Context(), oauth2.StaticTokenSource(state.Token)) memberships, err := api.GithubOAuth2Config.ListOrganizationMemberships(r.Context(), oauthClient) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching authenticated Github user organizations.", Detail: err.Error(), }) @@ -66,7 +66,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { } } if selectedMembership == nil { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "You aren't a member of the authorized Github organizations!", }) return @@ -76,7 +76,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { if len(api.GithubOAuth2Config.AllowTeams) > 0 { teams, err := api.GithubOAuth2Config.ListTeams(r.Context(), oauthClient, *selectedMembership.Organization.Login) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Failed to fetch teams from GitHub.", Detail: err.Error(), }) @@ -100,7 +100,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { } if allowedTeam == nil { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("You aren't a member of an authorized team in the %s Github organization!", *selectedMembership.Organization.Login), }) return @@ -109,7 +109,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching personal Github user.", Detail: err.Error(), }) @@ -130,14 +130,14 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { continue } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Internal error fetching user by email %q.", *email.Email), Detail: err.Error(), }) return } if !*email.Verified { - httpapi.Write(rw, http.StatusForbidden, httpapi.Response{ + httpapi.Write(rw, http.StatusForbidden, codersdk.Response{ Message: fmt.Sprintf("Verify the %q email address on Github to authenticate!", *email.Email), }) return @@ -148,7 +148,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { // If the user doesn't exist, create a new one! if user.ID == uuid.Nil { if !api.GithubOAuth2Config.AllowSignups { - httpapi.Write(rw, http.StatusForbidden, httpapi.Response{ + httpapi.Write(rw, http.StatusForbidden, codersdk.Response{ Message: "Signups are disabled for Github authentication!", }) return @@ -164,7 +164,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching authenticated Github user.", Detail: err.Error(), }) @@ -179,7 +179,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { break } if verifiedEmail == nil { - httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionRequired, codersdk.Response{ Message: "Your primary email must be verified on GitHub!", }) return @@ -190,7 +190,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { OrganizationID: organizationID, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error creating user.", Detail: err.Error(), }) diff --git a/coderd/users.go b/coderd/users.go index b52e85ad36ccd..91f9676b445b1 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -33,7 +33,7 @@ import ( 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user count.", Detail: err.Error(), }) @@ -41,13 +41,13 @@ func (api *API) firstUser(rw http.ResponseWriter, r *http.Request) { } if userCount == 0 { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "The initial user has not been created!", }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "The initial user has already been created!", }) } @@ -62,7 +62,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) { // This should only function for the first user. userCount, err := api.Database.GetUserCount(r.Context()) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user count.", Detail: err.Error(), }) @@ -71,7 +71,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{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: "The initial user has already been created.", }) return @@ -83,7 +83,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) { Password: createUser.Password, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error creating user.", Detail: err.Error(), }) @@ -106,7 +106,7 @@ func (api *API) postFirstUser(rw http.ResponseWriter, r *http.Request) { ID: user.ID, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating user's roles.", Detail: err.Error(), }) @@ -123,7 +123,7 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) { query := r.URL.Query().Get("q") params, errs := userSearchQuery(query) if len(errs) > 0 { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid user search query.", Validations: errs, }) @@ -148,7 +148,7 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching users.", Detail: err.Error(), }) @@ -165,7 +165,7 @@ func (api *API) users(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -207,13 +207,13 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) { Email: createUser.Email, }) if err == nil { - httpapi.Write(rw, http.StatusConflict, httpapi.Response{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: "User already exists.", }) return } if !errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -222,13 +222,13 @@ 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{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching organization.", Detail: err.Error(), }) @@ -237,7 +237,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error creating user.", Detail: err.Error(), }) @@ -264,7 +264,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) { } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -292,21 +292,21 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { isDifferentUser := existentUser.ID != user.ID if err == nil && isDifferentUser { - responseErrors := []httpapi.Error{} + responseErrors := []codersdk.Error{} if existentUser.Username == params.Username { - responseErrors = append(responseErrors, httpapi.Error{ + responseErrors = append(responseErrors, codersdk.Error{ Field: "username", Detail: "this value is already in use and should be unique", }) } - httpapi.Write(rw, http.StatusConflict, httpapi.Response{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: "User already exists.", Validations: responseErrors, }) return } if !errors.Is(err, sql.ErrNoRows) && isDifferentUser { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -321,7 +321,7 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating user.", Detail: err.Error(), }) @@ -330,7 +330,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -351,7 +351,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW } if status == database.UserStatusSuspended && user.ID == apiKey.UserID { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "You cannot suspend yourself.", }) return @@ -364,7 +364,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Internal error updating user's status to %q.", status), Detail: err.Error(), }) @@ -373,7 +373,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -401,9 +401,9 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid password.", - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ { Field: "password", Detail: err.Error(), @@ -423,16 +423,16 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { // if they send something let's validate it ok, err := userpassword.Compare(string(user.HashedPassword), params.OldPassword) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error with passwords.", Detail: err.Error(), }) return } if !ok { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Old password is incorrect.", - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ { Field: "old_password", Detail: "Old password is incorrect.", @@ -445,7 +445,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error hashing new password.", Detail: err.Error(), }) @@ -456,7 +456,7 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { HashedPassword: []byte(hashedPassword), }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating user's password.", Detail: err.Error(), }) @@ -481,7 +481,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organization memberships.", Detail: err.Error(), }) @@ -508,7 +508,7 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) { apiKey := httpmw.APIKey(r) if apiKey.UserID == user.ID { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "You cannot change your own roles.", }) return @@ -547,7 +547,7 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) { ID: user.ID, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: err.Error(), }) return @@ -555,7 +555,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -596,7 +596,7 @@ func (api *API) organizationsByUser(rw http.ResponseWriter, r *http.Request) { organizations = []database.Organization{} } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user's organizations.", Detail: err.Error(), }) @@ -622,7 +622,7 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching organization.", Detail: err.Error(), }) @@ -651,7 +651,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { Email: loginWithPassword.Email, }) if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error.", }) return @@ -660,7 +660,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { // If the user doesn't exist, it will be a default struct. equal, err := userpassword.Compare(string(user.HashedPassword), loginWithPassword.Password) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error.", }) return @@ -668,7 +668,7 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { if !equal { // This message is the same as above to remove ease in detecting whether // users are registered or not. Attackers still could with a timing attack. - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Incorrect email or password.", }) return @@ -676,7 +676,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{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Your account is suspended. Contact an admin to reactivate your account.", }) return @@ -738,7 +738,7 @@ func (api *API) apiKey(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching API key.", Detail: err.Error(), }) @@ -764,14 +764,14 @@ func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) { apiKey := httpmw.APIKey(r) err := api.Database.DeleteAPIKeyByID(r.Context(), apiKey.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error deleting API key.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Logged out!", }) } @@ -794,7 +794,7 @@ func generateAPIKeyIDSecret() (id string, secret string, err error) { func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params database.InsertAPIKeyParams) (string, bool) { keyID, keySecret, err := generateAPIKeyIDSecret() if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error generating API key.", Detail: err.Error(), }) @@ -840,7 +840,7 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat OAuthExpiry: params.OAuthExpiry, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting API key.", Detail: err.Error(), }) @@ -985,7 +985,7 @@ func findUser(id uuid.UUID, users []database.User) *database.User { return nil } -func userSearchQuery(query string) (database.GetUsersParams, []httpapi.Error) { +func userSearchQuery(query string) (database.GetUsersParams, []codersdk.Error) { searchParams := make(url.Values) if query == "" { // No filter @@ -1005,7 +1005,7 @@ func userSearchQuery(query string) (database.GetUsersParams, []httpapi.Error) { case 2: searchParams.Set(parts[0], parts[1]) default: - return database.GetUsersParams{}, []httpapi.Error{ + return database.GetUsersParams{}, []codersdk.Error{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 ':'", element)}, } } diff --git a/coderd/users_test.go b/coderd/users_test.go index d8bd106127a81..2fe023fbb19c7 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -37,7 +37,7 @@ func TestFirstUser(t *testing.T) { Password: "password", OrganizationName: "someorg", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -58,7 +58,7 @@ func TestPostLogin(t *testing.T) { Email: "my@email.org", Password: "password", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -78,7 +78,7 @@ func TestPostLogin(t *testing.T) { Email: req.Email, Password: "badpass", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -97,7 +97,7 @@ func TestPostLogin(t *testing.T) { // Test an existing session _, err = member.User(context.Background(), codersdk.Me) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) require.Contains(t, apiErr.Message, "Contact an admin") @@ -187,7 +187,7 @@ func TestPostLogout(t *testing.T) { require.Equal(t, -1, cookies[0].MaxAge, "Cookie should be set to delete") _, err = client.GetAPIKey(ctx, admin.UserID.String(), keyID) - var sdkErr = &codersdk.Error{} + var sdkErr = &codersdk.HTTPError{} require.ErrorAs(t, err, &sdkErr) require.Equal(t, http.StatusUnauthorized, sdkErr.StatusCode(), "Expecting 401") }) @@ -214,7 +214,7 @@ func TestPostUsers(t *testing.T) { Password: "password", OrganizationID: uuid.New(), }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -229,7 +229,7 @@ func TestPostUsers(t *testing.T) { Username: "someone-else", Password: "testing", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -251,7 +251,7 @@ func TestPostUsers(t *testing.T) { Password: "testing", OrganizationID: org.ID, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusForbidden, apiErr.StatusCode()) }) @@ -279,7 +279,7 @@ func TestUpdateUserProfile(t *testing.T) { _, err := client.UpdateUserProfile(context.Background(), uuid.New().String(), codersdk.UpdateUserProfileRequest{ Username: "newusername", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) // Right now, we are raising a BAD request error because we don't support a // user accessing other users info @@ -300,7 +300,7 @@ func TestUpdateUserProfile(t *testing.T) { _, err = client.UpdateUserProfile(context.Background(), codersdk.Me, codersdk.UpdateUserProfileRequest{ Username: existentUser.Username, }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -391,7 +391,7 @@ func TestGrantRoles(t *testing.T) { requireStatusCode := func(t *testing.T, err error, statusCode int) { t.Helper() - var e *codersdk.Error + var e *codersdk.HTTPError require.ErrorAs(t, err, &e, "error is codersdk error") require.Equal(t, statusCode, e.StatusCode(), "correct status code") } @@ -844,7 +844,7 @@ func TestPostAPIKey(t *testing.T) { client.SessionToken = "" _, err := client.CreateAPIKey(context.Background(), codersdk.Me) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index a434a55700074..fca8fae262d5b 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -44,7 +44,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { } dbApps, err := api.Database.GetWorkspaceAppsByAgentID(r.Context(), workspaceAgent.ID) if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agent applications.", Detail: err.Error(), }) @@ -52,7 +52,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { } apiAgent, err := convertWorkspaceAgent(workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", Detail: err.Error(), }) @@ -76,14 +76,14 @@ func (api *API) workspaceAgentDial(rw http.ResponseWriter, r *http.Request) { } apiAgent, err := convertWorkspaceAgent(workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", Detail: err.Error(), }) return } if apiAgent.Status != codersdk.WorkspaceAgentConnected { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: fmt.Sprintf("Agent isn't connected! Status: %s.", apiAgent.Status), }) return @@ -91,7 +91,7 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -123,7 +123,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) workspaceAgent := httpmw.WorkspaceAgent(r) apiAgent, err := convertWorkspaceAgent(workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", Detail: err.Error(), }) @@ -131,7 +131,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) } resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), workspaceAgent.ResourceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace resources.", Detail: err.Error(), }) @@ -139,7 +139,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) } build, err := api.Database.GetWorkspaceBuildByJobID(r.Context(), resource.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -147,7 +147,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) } workspace, err := api.Database.GetWorkspaceByID(r.Context(), build.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace.", Detail: err.Error(), }) @@ -155,7 +155,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) } owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace owner.", Detail: err.Error(), }) @@ -164,7 +164,7 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request) ipp, ok := netaddr.FromStdIPNet(&workspaceAgent.WireguardNodeIPv6.IPNet) if !ok { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Workspace agent has an invalid ipv6 address.", Detail: workspaceAgent.WireguardNodeIPv6.IPNet.String(), }) @@ -190,7 +190,7 @@ func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) { workspaceAgent := httpmw.WorkspaceAgent(r) resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), workspaceAgent.ResourceID) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -199,7 +199,7 @@ func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) { build, err := api.Database.GetWorkspaceBuildByJobID(r.Context(), resource.JobID) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Internal error fetching workspace build job.", Detail: err.Error(), }) @@ -224,7 +224,7 @@ func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) { slog.F("resource", resource), slog.F("agent", workspaceAgent), ) - httpapi.Write(rw, http.StatusForbidden, httpapi.Response{ + httpapi.Write(rw, http.StatusForbidden, codersdk.Response{ Message: "Agent trying to connect from non-latest build.", Detail: err.Error(), }) @@ -235,7 +235,7 @@ func (api *API) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) { CompressionMode: websocket.CompressionDisabled, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -350,7 +350,7 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) { // By default requests have the remote address and port. host, port, err := net.SplitHostPort(r.RemoteAddr) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid remote address.", Detail: err.Error(), }) @@ -359,7 +359,7 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) { remoteAddress.IP = net.ParseIP(host) remoteAddress.Port, err = strconv.Atoi(port) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Port for remote address %q must be an integer.", r.RemoteAddr), Detail: err.Error(), }) @@ -370,7 +370,7 @@ func (api *API) workspaceAgentTurn(rw http.ResponseWriter, r *http.Request) { CompressionMode: websocket.CompressionDisabled, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -404,14 +404,14 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { } apiAgent, err := convertWorkspaceAgent(workspaceAgent, nil, api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", Detail: err.Error(), }) return } if apiAgent.Status != codersdk.WorkspaceAgentConnected { - httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionRequired, codersdk.Response{ Message: fmt.Sprintf("Agent state is %q, it must be in the %q state.", apiAgent.Status, codersdk.WorkspaceAgentConnected), }) return @@ -419,9 +419,9 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param 'reconnect' must be a valid UUID.", - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "reconnect", Detail: "invalid UUID"}, }, }) @@ -440,7 +440,7 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { CompressionMode: websocket.CompressionDisabled, }) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) @@ -495,7 +495,7 @@ func (api *API) postWorkspaceAgentKeys(rw http.ResponseWriter, r *http.Request) UpdatedAt: database.Now(), }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error setting agent keys.", Detail: err.Error(), }) @@ -522,7 +522,7 @@ func (api *API) postWorkspaceAgentWireguardPeer(rw http.ResponseWriter, r *http. } if req.Recipient != workspaceAgent.ID { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid recipient.", }) return @@ -530,7 +530,7 @@ func (api *API) postWorkspaceAgentWireguardPeer(rw http.ResponseWriter, r *http. raw, err := req.MarshalText() if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error marshaling wireguard peer message.", Detail: err.Error(), }) @@ -539,7 +539,7 @@ func (api *API) postWorkspaceAgentWireguardPeer(rw http.ResponseWriter, r *http. err = api.Pubsub.Publish("wireguard_peers", raw) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error publishing wireguard peer message.", Detail: err.Error(), }) @@ -560,7 +560,7 @@ func (api *API) workspaceAgentWireguardListener(rw http.ResponseWriter, r *http. conn, err := websocket.Accept(rw, r, nil) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to accept websocket.", Detail: err.Error(), }) diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index fcdd7ceb5f523..f08e9d77c01e4 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/rbac" + "github.com/coder/coder/codersdk" "github.com/coder/coder/site" ) @@ -36,7 +37,7 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace.", Detail: err.Error(), }) @@ -49,7 +50,7 @@ func (api *API) workspaceAppsProxyPath(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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -58,7 +59,7 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) resources, err := api.Database.GetWorkspaceResourcesByJobID(r.Context(), build.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace resources.", Detail: err.Error(), }) @@ -70,14 +71,14 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) } agents, err := api.Database.GetWorkspaceAgentsByResourceIDs(r.Context(), resourceIDs) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agents.", Detail: err.Error(), }) return } if len(agents) == 0 { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "No agents exist.", }) return @@ -98,20 +99,20 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) Name: chi.URLParam(r, "workspaceapp"), }) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: "Application not found.", }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace application.", Detail: err.Error(), }) return } if !app.Url.Valid { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Application %s does not have a url.", app.Name), }) return @@ -119,7 +120,7 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) appURL, err := url.Parse(app.Url.String) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("App url %q must be a valid url.", app.Url.String), Detail: err.Error(), }) @@ -160,7 +161,7 @@ func (api *API) workspaceAppsProxyPath(rw http.ResponseWriter, r *http.Request) conn, release, err := api.workspaceAgentCache.Acquire(r, agent.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Failed to dial workspace agent.", Detail: err.Error(), }) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index f62a113d040d9..c43911ffd1795 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -32,7 +32,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -41,7 +41,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -75,12 +75,12 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { // query will not work. _, err := store.GetWorkspaceBuildByID(r.Context(), paginationParams.AfterID) if err != nil && xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build at \"after_id\".", Detail: err.Error(), }) @@ -99,7 +99,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -121,7 +121,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner jobs.", Detail: err.Error(), }) @@ -138,7 +138,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { } users, err := api.Database.GetUsersByIDs(r.Context(), userIDs) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -149,7 +149,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { for _, build := range builds { job, exists := jobByID[build.JobID.String()] if !exists { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Job %q doesn't exist for build %q.", build.JobID, build.ID), }) return @@ -167,7 +167,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ workspaceName := chi.URLParam(r, "workspacename") buildNumber, err := strconv.ParseInt(chi.URLParam(r, "buildnumber"), 10, 32) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Failed to parse build number as integer.", Detail: err.Error(), }) @@ -183,7 +183,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace by name.", Detail: err.Error(), }) @@ -200,13 +200,13 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ BuildNumber: int32(buildNumber), }) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Workspace %q Build %d does not exist.", workspaceName, buildNumber), }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -215,7 +215,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceBuild.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -224,7 +224,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -254,7 +254,7 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build by name.", Detail: err.Error(), }) @@ -262,7 +262,7 @@ func (api *API) workspaceBuildByName(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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -270,7 +270,7 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) { } users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error getting user.", Detail: err.Error(), }) @@ -298,7 +298,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { case codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop: action = rbac.ActionUpdate default: - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Transition %q not supported.", createBuild.Transition), }) return @@ -312,7 +312,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { if createBuild.TemplateVersionID == uuid.Nil { latestBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching the latest workspace build.", Detail: err.Error(), }) @@ -322,9 +322,9 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Template version not found.", - Validations: []httpapi.Error{{ + Validations: []codersdk.Error{{ Field: "template_version_id", Detail: "template version not found", }}, @@ -332,7 +332,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) @@ -340,7 +340,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { } templateVersionJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -349,17 +349,17 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { templateVersionJobStatus := convertProvisionerJob(templateVersionJob).Status switch templateVersionJobStatus { case codersdk.ProvisionerJobPending, codersdk.ProvisionerJobRunning: - httpapi.Write(rw, http.StatusNotAcceptable, httpapi.Response{ + httpapi.Write(rw, http.StatusNotAcceptable, codersdk.Response{ Message: fmt.Sprintf("The provided template version is %s. Wait for it to complete importing!", templateVersionJobStatus), }) return case codersdk.ProvisionerJobFailed: - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: fmt.Sprintf("The provided template version %q has failed to import: %q. You cannot build workspaces with it!", templateVersion.Name, templateVersionJob.Error.String), }) return case codersdk.ProvisionerJobCanceled: - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "The provided template version was canceled during import. You cannot builds workspaces with it!", }) return @@ -367,7 +367,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template job.", Detail: err.Error(), }) @@ -380,7 +380,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { if err == nil { priorJob, err := api.Database.GetProvisionerJobByID(r.Context(), priorHistory.JobID) if err == nil && convertProvisionerJob(priorJob).Status.Active() { - httpapi.Write(rw, http.StatusConflict, httpapi.Response{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: "A workspace build is already active.", }) return @@ -388,7 +388,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching prior workspace build.", Detail: err.Error(), }) @@ -485,7 +485,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error inserting workspace build.", Detail: err.Error(), }) @@ -497,7 +497,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { workspaceBuild.InitiatorID, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error getting user.", Detail: err.Error(), }) @@ -513,7 +513,7 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques workspaceBuild := httpmw.WorkspaceBuildParam(r) workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "No workspace exists for this job.", }) return @@ -527,20 +527,20 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already completed!", }) return } if job.CanceledAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job has already been marked as canceled!", }) return @@ -553,13 +553,13 @@ func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Reques }, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating provisioner job.", Detail: err.Error(), }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Job has been marked as canceled...", }) } @@ -568,7 +568,7 @@ func (api *API) workspaceBuildResources(rw http.ResponseWriter, r *http.Request) workspaceBuild := httpmw.WorkspaceBuildParam(r) workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "No workspace exists for this job.", }) return @@ -582,7 +582,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -595,7 +595,7 @@ func (api *API) workspaceBuildLogs(rw http.ResponseWriter, r *http.Request) { workspaceBuild := httpmw.WorkspaceBuildParam(r) workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "No workspace exists for this job.", }) return @@ -609,7 +609,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -622,7 +622,7 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { workspaceBuild := httpmw.WorkspaceBuildParam(r) workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspaceBuild.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "No workspace exists for this job.", }) return diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index 9ee40a6460525..955c860af3577 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -67,7 +67,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { workspace.Name, "buildNumber", ) - var apiError *codersdk.Error + var apiError *codersdk.HTTPError require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusBadRequest, apiError.StatusCode()) require.ErrorContains(t, apiError, "Failed to parse build number as integer.") @@ -89,7 +89,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { "workspaceName", strconv.FormatInt(int64(workspace.LatestBuild.BuildNumber), 10), ) - var apiError *codersdk.Error + var apiError *codersdk.HTTPError require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusNotFound, apiError.StatusCode()) require.ErrorContains(t, apiError, "Resource not found") @@ -111,7 +111,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { workspace.Name, "200", ) - var apiError *codersdk.Error + var apiError *codersdk.HTTPError require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusNotFound, apiError.StatusCode()) require.ErrorContains(t, apiError, fmt.Sprintf("Workspace %q Build 200 does not exist.", workspace.Name)) @@ -156,7 +156,7 @@ func TestWorkspaceBuilds(t *testing.T) { AfterID: uuid.New(), }, }) - var apiError *codersdk.Error + var apiError *codersdk.HTTPError require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusBadRequest, apiError.StatusCode()) require.Contains(t, apiError.Message, "does not exist") @@ -246,7 +246,7 @@ func TestWorkspaceBuildResources(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _, err := client.WorkspaceResourcesByBuild(context.Background(), workspace.LatestBuild.ID) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index 0b21aa6545b87..048edeaba4012 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -25,7 +25,7 @@ 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{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid Azure identity.", Detail: err.Error(), }) @@ -44,7 +44,7 @@ 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{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid AWS identity.", Detail: err.Error(), }) @@ -65,7 +65,7 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, // We leave the audience blank. It's not important we validate who made the token. payload, err := api.GoogleTokenValidator.Validate(r.Context(), req.JSONWebToken, "") if err != nil { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid GCP identity.", Detail: err.Error(), }) @@ -80,7 +80,7 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, }{} err = mapstructure.Decode(payload.Claims, &claims) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Error decoding JWT claims.", Detail: err.Error(), }) @@ -92,13 +92,13 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, instanceID string) { agent, err := api.Database.GetWorkspaceAgentByInstanceID(r.Context(), instanceID) if errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ + httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Instance with id %q not found.", instanceID), }) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job agent.", Detail: err.Error(), }) @@ -106,7 +106,7 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in } resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job resource.", Detail: err.Error(), }) @@ -114,14 +114,14 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in } job, err := api.Database.GetProvisionerJobByID(r.Context(), resource.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if job.Type != database.ProvisionerJobTypeWorkspaceBuild { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("%q jobs cannot be authenticated.", job.Type), }) return @@ -129,7 +129,7 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in var jobData workspaceProvisionJob err = json.Unmarshal(job.Input, &jobData) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error extracting job data.", Detail: err.Error(), }) @@ -137,7 +137,7 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in } resourceHistory, err := api.Database.GetWorkspaceBuildByID(r.Context(), jobData.WorkspaceBuildID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -148,14 +148,14 @@ func (api *API) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in // we'd hate to leak access to a user's workspace. latestHistory, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), resourceHistory.WorkspaceID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching the latest workspace build.", Detail: err.Error(), }) return } if latestHistory.ID != resourceHistory.ID { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Resource found for id %q, but isn't registered on the latest history.", instanceID), }) return diff --git a/coderd/workspaceresourceauth_test.go b/coderd/workspaceresourceauth_test.go index 04374c1821dc4..6cf6b3ab6d2a7 100644 --- a/coderd/workspaceresourceauth_test.go +++ b/coderd/workspaceresourceauth_test.go @@ -100,7 +100,7 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { GoogleTokenValidator: validator, }) _, err := client.AuthWorkspaceGoogleInstanceIdentity(context.Background(), "", metadata) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -113,7 +113,7 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { GoogleTokenValidator: validator, }) _, err := client.AuthWorkspaceGoogleInstanceIdentity(context.Background(), "", metadata) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/workspaceresources.go b/coderd/workspaceresources.go index 66dcc89bfb977..eb1ecea27fb62 100644 --- a/coderd/workspaceresources.go +++ b/coderd/workspaceresources.go @@ -25,14 +25,14 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) return } if !job.CompletedAt.Valid { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "Job hasn't completed!", }) return @@ -42,7 +42,7 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) { err = nil } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job agents.", Detail: err.Error(), }) @@ -54,7 +54,7 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) { } apps, err := api.Database.GetWorkspaceAppsByAgentIDs(r.Context(), agentIDs) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace agent applications.", Detail: err.Error(), }) @@ -71,7 +71,7 @@ func (api *API) workspaceResource(rw http.ResponseWriter, r *http.Request) { convertedAgent, err := convertWorkspaceAgent(agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace agent.", Detail: err.Error(), }) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index ef4a07805a1a5..58f88cbd5fea3 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -49,9 +49,9 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { var err error showDeleted, err = strconv.ParseBool(deletedStr) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", deletedStr), - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "deleted", Detail: "Must be a valid boolean"}, }, }) @@ -59,7 +59,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { } } if workspace.Deleted && !showDeleted { - httpapi.Write(rw, http.StatusGone, httpapi.Response{ + httpapi.Write(rw, http.StatusGone, codersdk.Response{ Message: fmt.Sprintf("Workspace %q was deleted, you can view this workspace by specifying '?deleted=true' and trying again.", workspace.ID.String()), }) return @@ -67,7 +67,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -93,7 +93,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { }) err = group.Wait() if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching resource.", Detail: err.Error(), }) @@ -112,7 +112,7 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { queryStr := r.URL.Query().Get("q") filter, errs := workspaceSearchQuery(queryStr) if len(errs) > 0 { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid workspace search query.", Validations: errs, }) @@ -126,7 +126,7 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { workspaces, err := api.Database.GetWorkspaces(r.Context(), filter) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspaces.", Detail: err.Error(), }) @@ -138,7 +138,7 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) { apiWorkspaces, err := convertWorkspaces(r.Context(), api.Database, workspaces) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error reading workspace.", Detail: err.Error(), }) @@ -156,9 +156,9 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) var err error includeDeleted, err = strconv.ParseBool(s) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", s), - Validations: []httpapi.Error{ + Validations: []codersdk.Error{ {Field: "include_deleted", Detail: "Must be a valid boolean"}, }, }) @@ -182,7 +182,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace by name.", Detail: err.Error(), }) @@ -195,7 +195,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -203,7 +203,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) } job, err := api.Database.GetProvisionerJobByID(r.Context(), build.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching provisioner job.", Detail: err.Error(), }) @@ -211,7 +211,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) } template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) @@ -220,7 +220,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) initiator, err := api.Database.GetUserByID(r.Context(), build.InitiatorID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) @@ -247,9 +247,9 @@ 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{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Template %q doesn't exist.", createWorkspace.TemplateID.String()), - Validations: []httpapi.Error{{ + Validations: []codersdk.Error{{ Field: "template_id", Detail: "template not found", }}, @@ -257,7 +257,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template.", Detail: err.Error(), }) @@ -270,7 +270,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req } if organization.ID != template.OrganizationID { - httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{ + httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ Message: fmt.Sprintf("Template is not in organization %q.", organization.Name), }) return @@ -278,18 +278,18 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req dbAutostartSchedule, err := validWorkspaceSchedule(createWorkspace.AutostartSchedule, time.Duration(template.MinAutostartInterval)) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Autostart Schedule.", - Validations: []httpapi.Error{{Field: "schedule", Detail: err.Error()}}, + Validations: []codersdk.Error{{Field: "schedule", Detail: err.Error()}}, }) return } dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, time.Duration(template.MaxTtl)) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace TTL.", - Validations: []httpapi.Error{{Field: "ttl_ms", Detail: err.Error()}}, + Validations: []codersdk.Error{{Field: "ttl_ms", Detail: err.Error()}}, }) return } @@ -307,16 +307,16 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req // If the workspace already exists, don't allow creation. template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ 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{ + httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: fmt.Sprintf("Workspace %q already exists in the %q template.", createWorkspace.Name, template.Name), - Validations: []httpapi.Error{{ + Validations: []codersdk.Error{{ Field: "name", Detail: "this value is already in use and should be unique", }}, @@ -324,7 +324,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } if !errors.Is(err, sql.ErrNoRows) { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: fmt.Sprintf("Internal error fetching workspace by name %q.", createWorkspace.Name), Detail: err.Error(), }) @@ -333,7 +333,7 @@ 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{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version.", Detail: err.Error(), }) @@ -341,7 +341,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req } templateVersionJob, err := api.Database.GetProvisionerJobByID(r.Context(), templateVersion.JobID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching template version job.", Detail: err.Error(), }) @@ -350,17 +350,17 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req templateVersionJobStatus := convertProvisionerJob(templateVersionJob).Status switch templateVersionJobStatus { case codersdk.ProvisionerJobPending, codersdk.ProvisionerJobRunning: - httpapi.Write(rw, http.StatusNotAcceptable, httpapi.Response{ + httpapi.Write(rw, http.StatusNotAcceptable, codersdk.Response{ Message: fmt.Sprintf("The provided template version is %s. Wait for it to complete importing!", templateVersionJobStatus), }) return case codersdk.ProvisionerJobFailed: - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: fmt.Sprintf("The provided template version %q has failed to import. You cannot create workspaces using it!", templateVersion.Name), }) return case codersdk.ProvisionerJobCanceled: - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + httpapi.Write(rw, http.StatusPreconditionFailed, codersdk.Response{ Message: "The provided template version was canceled during import. You cannot create workspaces using it!", }) return @@ -444,7 +444,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return nil }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error creating workspace.", Detail: err.Error(), }) @@ -452,7 +452,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req } users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{apiKey.UserID, workspaceBuild.InitiatorID}) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching user.", Detail: err.Error(), }) @@ -483,7 +483,7 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID) if err != nil { api.Logger.Error(r.Context(), "fetch workspace template", slog.F("workspace_id", workspace.ID), slog.F("template_id", workspace.TemplateID), slog.Error(err)) - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Error fetching workspace template.", }) return @@ -491,9 +491,9 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { dbSched, err := validWorkspaceSchedule(req.Schedule, time.Duration(template.MinAutostartInterval)) if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid autostart schedule.", - Validations: []httpapi.Error{{Field: "schedule", Detail: err.Error()}}, + Validations: []codersdk.Error{{Field: "schedule", Detail: err.Error()}}, }) return } @@ -503,7 +503,7 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { AutostartSchedule: dbSched, }) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error updating workspace autostart schedule.", Detail: err.Error(), }) @@ -523,12 +523,12 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { return } - var validErrs []httpapi.Error + var validErrs []codersdk.Error err := api.Database.InTx(func(s database.Store) error { template, err := s.GetTemplateByID(r.Context(), workspace.TemplateID) if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{ Message: "Error fetching workspace template!", }) return xerrors.Errorf("fetch workspace template: %w", err) @@ -536,7 +536,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { dbTTL, err := validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.MaxTtl)) if err != nil { - validErrs = append(validErrs, httpapi.Error{Field: "ttl_ms", Detail: err.Error()}) + validErrs = append(validErrs, codersdk.Error{Field: "ttl_ms", Detail: err.Error()}) return err } if err := s.UpdateWorkspaceTTL(r.Context(), database.UpdateWorkspaceTTLParams{ @@ -554,7 +554,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { if len(validErrs) > 0 { code = http.StatusBadRequest } - httpapi.Write(rw, code, httpapi.Response{ + httpapi.Write(rw, code, codersdk.Response{ Message: "Error updating workspace time until shutdown!", Validations: validErrs, Detail: err.Error(), @@ -579,7 +579,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { } code := http.StatusOK - resp := httpapi.Response{} + resp := codersdk.Response{} err := api.Database.InTx(func(s database.Store) error { template, err := s.GetTemplateByID(r.Context(), workspace.TemplateID) @@ -625,7 +625,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline, time.Duration(template.MaxTtl)); err != nil { code = http.StatusBadRequest resp.Message = "Bad extend workspace request." - resp.Validations = append(resp.Validations, httpapi.Error{Field: "deadline", Detail: err.Error()}) + resp.Validations = append(resp.Validations, codersdk.Error{Field: "deadline", Detail: err.Error()}) return err } @@ -698,7 +698,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { case <-t.C: workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspace.ID) if err != nil { - _ = wsjson.Write(ctx, c, httpapi.Response{ + _ = wsjson.Write(ctx, c, codersdk.Response{ Message: "Internal error fetching workspace.", Detail: err.Error(), }) @@ -706,7 +706,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { } build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID) if err != nil { - _ = wsjson.Write(ctx, c, httpapi.Response{ + _ = wsjson.Write(ctx, c, codersdk.Response{ Message: "Internal error fetching workspace build.", Detail: err.Error(), }) @@ -732,7 +732,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { }) err = group.Wait() if err != nil { - _ = wsjson.Write(ctx, c, httpapi.Response{ + _ = wsjson.Write(ctx, c, codersdk.Response{ Message: "Internal error fetching resource.", Detail: err.Error(), }) @@ -953,7 +953,7 @@ func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error // workspaceSearchQuery takes a query string and returns the workspace filter. // It also can return the list of validation errors to return to the api. -func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []httpapi.Error) { +func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersdk.Error) { searchParams := make(url.Values) if query == "" { // No filter @@ -977,14 +977,14 @@ func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []httpapi searchParams.Set("owner", parts[0]) searchParams.Set("name", parts[1]) default: - return database.GetWorkspacesParams{}, []httpapi.Error{ + return database.GetWorkspacesParams{}, []codersdk.Error{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 '/'", element)}, } } case 2: searchParams.Set(parts[0], parts[1]) default: - return database.GetWorkspacesParams{}, []httpapi.Error{ + return database.GetWorkspacesParams{}, []codersdk.Error{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 ':'", element)}, } } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 2d607a5e3fa0f..90d13854a8e16 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -112,7 +112,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: "workspace", }) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -135,7 +135,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: "workspace", }) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -153,7 +153,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: workspace.Name, }) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -202,7 +202,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -224,7 +224,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -247,7 +247,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -262,7 +262,7 @@ func TestWorkspaceByOwnerAndName(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) _, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something", codersdk.WorkspaceOptions{}) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -634,7 +634,7 @@ func TestPostWorkspaceBuild(t *testing.T) { Transition: codersdk.WorkspaceTransitionStart, }) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -652,7 +652,7 @@ func TestPostWorkspaceBuild(t *testing.T) { TemplateID: template.ID, Name: "workspace", }) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -674,7 +674,7 @@ func TestPostWorkspaceBuild(t *testing.T) { Transition: codersdk.WorkspaceTransitionStart, }) require.Error(t, err) - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -754,7 +754,7 @@ func TestWorkspaceBuildByName(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _, err := client.WorkspaceBuildByName(context.Background(), workspace.ID, "something") - var apiErr *codersdk.Error + var apiErr *codersdk.HTTPError require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -905,8 +905,8 @@ func TestWorkspaceUpdateAutostart(t *testing.T) { ) err := client.UpdateWorkspaceAutostart(ctx, wsid, req) - require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error") - coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint + require.IsType(t, err, &codersdk.HTTPError{}, "expected codersdk.Error") + coderSDKErr, _ := err.(*codersdk.HTTPError) //nolint:errorlint require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404") require.Contains(t, coderSDKErr.Message, "Resource not found", "unexpected response code") }) @@ -1012,8 +1012,8 @@ func TestWorkspaceUpdateTTL(t *testing.T) { ) err := client.UpdateWorkspaceTTL(ctx, wsid, req) - require.IsType(t, err, &codersdk.Error{}, "expected codersdk.Error") - coderSDKErr, _ := err.(*codersdk.Error) //nolint:errorlint + require.IsType(t, err, &codersdk.HTTPError{}, "expected codersdk.Error") + coderSDKErr, _ := err.(*codersdk.HTTPError) //nolint:errorlint require.Equal(t, coderSDKErr.StatusCode(), 404, "expected status code 404") require.Contains(t, coderSDKErr.Message, "Resource not found", "unexpected response code") }) diff --git a/codersdk/client.go b/codersdk/client.go index 78a266db02202..36693540a7d3c 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -13,11 +13,11 @@ import ( "golang.org/x/xerrors" "nhooyr.io/websocket" - - "github.com/coder/coder/coderd/httpapi" - "github.com/coder/coder/coderd/httpmw" ) +// SessionTokenKey represents the name of the cookie or query parameter the API key is stored in. +const SessionTokenKey = "session_token" + // New creates a Coder client for the provided URL. func New(serverURL *url.URL) *Client { return &Client{ @@ -64,7 +64,7 @@ func (c *Client) Request(ctx context.Context, method, path string, body interfac return nil, xerrors.Errorf("create request: %w", err) } req.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }) if body != nil { @@ -99,7 +99,7 @@ func (c *Client) dialWebsocket(ctx context.Context, path string) (*websocket.Con } apiURL.Path = path q := apiURL.Query() - q.Add(httpmw.SessionTokenKey, c.SessionToken) + q.Add(SessionTokenKey, c.SessionToken) apiURL.RawQuery = q.Encode() //nolint:bodyclose @@ -113,7 +113,7 @@ func (c *Client) dialWebsocket(ctx context.Context, path string) (*websocket.Con return conn, nil } -// readBodyAsError reads the response as an httpapi.Message, and +// readBodyAsError reads the response as an .Message, and // wraps it in a codersdk.Error type for easy marshaling. func readBodyAsError(res *http.Response) error { contentType := res.Header.Get("Content-Type") @@ -138,9 +138,9 @@ func readBodyAsError(res *http.Response) error { if err != nil { return xerrors.Errorf("read body: %w", err) } - return &Error{ + return &HTTPError{ statusCode: res.StatusCode, - Response: httpapi.Response{ + Response: Response{ Message: string(resp), }, Helper: helper, @@ -148,19 +148,19 @@ func readBodyAsError(res *http.Response) error { } //nolint:varnamelen - var m httpapi.Response + var m Response err := json.NewDecoder(res.Body).Decode(&m) if err != nil { if errors.Is(err, io.EOF) { // If no body is sent, we'll just provide the status code. - return &Error{ + return &HTTPError{ statusCode: res.StatusCode, Helper: helper, } } return xerrors.Errorf("decode body: %w", err) } - return &Error{ + return &HTTPError{ Response: m, statusCode: res.StatusCode, method: method, @@ -171,8 +171,8 @@ func readBodyAsError(res *http.Response) error { // Error represents an unaccepted or invalid request to the API. // @typescript-ignore Error -type Error struct { - httpapi.Response +type HTTPError struct { + Response statusCode int method string @@ -181,11 +181,11 @@ type Error struct { Helper string } -func (e *Error) StatusCode() int { +func (e *HTTPError) StatusCode() int { return e.statusCode } -func (e *Error) Error() string { +func (e *HTTPError) Error() string { var builder strings.Builder if e.method != "" && e.url != "" { _, _ = fmt.Fprintf(&builder, "%v %v: ", e.method, e.url) diff --git a/codersdk/error.go b/codersdk/error.go new file mode 100644 index 0000000000000..5de2977838e86 --- /dev/null +++ b/codersdk/error.go @@ -0,0 +1,27 @@ +package codersdk + +// Response represents a generic HTTP response. +type Response struct { + // 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,omitempty"` + // 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:"validations,omitempty"` +} + +// Error represents a scoped error to a user input. +type Error struct { + Field string `json:"field" validate:"required"` + Detail string `json:"detail" validate:"required"` +} diff --git a/codersdk/provisionerdaemons.go b/codersdk/provisionerdaemons.go index 584f4a6d6d102..7645cd093f6cf 100644 --- a/codersdk/provisionerdaemons.go +++ b/codersdk/provisionerdaemons.go @@ -14,8 +14,6 @@ import ( "github.com/google/uuid" "golang.org/x/xerrors" "nhooyr.io/websocket" - - "github.com/coder/coder/coderd/httpmw" ) type LogSource string @@ -120,7 +118,7 @@ func (c *Client) provisionerJobLogsAfter(ctx context.Context, path string, after return nil, xerrors.Errorf("create cookie jar: %w", err) } jar.SetCookies(followURL, []*http.Cookie{{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }}) httpClient := &http.Client{ diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 499286d0c91a3..2ee7973c225da 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -20,7 +20,6 @@ import ( "cdr.dev/slog" "github.com/coder/coder/agent" - "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/turnconn" "github.com/coder/coder/peer" "github.com/coder/coder/peer/peerwg" @@ -189,7 +188,7 @@ func (c *Client) ListenWorkspaceAgent(ctx context.Context, logger slog.Logger) ( return agent.Metadata{}, nil, xerrors.Errorf("create cookie jar: %w", err) } jar.SetCookies(serverURL, []*http.Cookie{{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }}) httpClient := &http.Client{ @@ -285,7 +284,7 @@ func (c *Client) WireguardPeerListener(ctx context.Context, logger slog.Logger) return nil, nil, xerrors.Errorf("create cookie jar: %w", err) } jar.SetCookies(serverURL, []*http.Cookie{{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }}) httpClient := &http.Client{ @@ -355,7 +354,7 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti return nil, xerrors.Errorf("create cookie jar: %w", err) } jar.SetCookies(serverURL, []*http.Cookie{{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }}) httpClient := &http.Client{ @@ -443,7 +442,7 @@ func (c *Client) WorkspaceAgentReconnectingPTY(ctx context.Context, agentID, rec return nil, xerrors.Errorf("create cookie jar: %w", err) } jar.SetCookies(serverURL, []*http.Cookie{{ - Name: httpmw.SessionTokenKey, + Name: SessionTokenKey, Value: c.SessionToken, }}) httpClient := &http.Client{ From eae76eb9d589d924aca2c2365b0ea04d7aaf43b7 Mon Sep 17 00:00:00 2001 From: sreya Date: Tue, 12 Jul 2022 21:43:37 +0000 Subject: [PATCH 2/6] fix some tests --- coderd/httpmw/apikey.go | 15 +++++-------- coderd/httpmw/apikey_test.go | 26 +++++++++++----------- coderd/httpmw/authorize_test.go | 3 ++- coderd/httpmw/organizationparam_test.go | 3 ++- coderd/httpmw/templateparam_test.go | 3 ++- coderd/httpmw/templateversionparam_test.go | 3 ++- coderd/httpmw/userparam_test.go | 3 ++- coderd/httpmw/workspaceagent.go | 4 ++-- coderd/httpmw/workspaceagent_test.go | 3 ++- coderd/httpmw/workspaceagentparam_test.go | 3 ++- coderd/httpmw/workspacebuildparam_test.go | 3 ++- coderd/httpmw/workspaceparam_test.go | 3 ++- 12 files changed, 39 insertions(+), 33 deletions(-) diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 364ebeadd2681..6634363e4ce9e 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -21,9 +21,6 @@ import ( "github.com/coder/coder/codersdk" ) -// SessionTokenKey represents the name of the cookie or query parameter the API key is stored in. -const SessionTokenKey = "session_token" - type apiKeyContextKey struct{} // APIKey returns the API key from the ExtractAPIKey handler. @@ -78,15 +75,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool } var cookieValue string - cookie, err := r.Cookie(SessionTokenKey) + cookie, err := r.Cookie(codersdk.SessionTokenKey) if err != nil { - cookieValue = r.URL.Query().Get(SessionTokenKey) + cookieValue = r.URL.Query().Get(codersdk.SessionTokenKey) } else { cookieValue = cookie.Value } if cookieValue == "" { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Cookie %q or query parameter must be provided.", SessionTokenKey), + Message: fmt.Sprintf("Cookie %q or query parameter must be provided.", codersdk.SessionTokenKey), }) return } @@ -94,7 +91,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // APIKeys are formatted: ID-SECRET if len(parts) != 2 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key format.", SessionTokenKey), + Message: fmt.Sprintf("Invalid %q cookie API key format.", codersdk.SessionTokenKey), }) return } @@ -103,13 +100,13 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs, redirectToLogin bool // Ensuring key lengths are valid. if len(keyID) != 10 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key id.", SessionTokenKey), + Message: fmt.Sprintf("Invalid %q cookie API key id.", codersdk.SessionTokenKey), }) return } if len(keySecret) != 22 { write(http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Invalid %q cookie API key secret.", SessionTokenKey), + Message: fmt.Sprintf("Invalid %q cookie API key secret.", codersdk.SessionTokenKey), }) return } diff --git a/coderd/httpmw/apikey_test.go b/coderd/httpmw/apikey_test.go index 5fa21fd9cd7b6..0d29c84653c6f 100644 --- a/coderd/httpmw/apikey_test.go +++ b/coderd/httpmw/apikey_test.go @@ -75,7 +75,7 @@ func TestAPIKey(t *testing.T) { rw = httptest.NewRecorder() ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: "test-wow-hello", }) @@ -93,7 +93,7 @@ func TestAPIKey(t *testing.T) { rw = httptest.NewRecorder() ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: "test-wow", }) @@ -111,7 +111,7 @@ func TestAPIKey(t *testing.T) { rw = httptest.NewRecorder() ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: "testtestid-wow", }) @@ -130,7 +130,7 @@ func TestAPIKey(t *testing.T) { rw = httptest.NewRecorder() ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -150,7 +150,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -179,7 +179,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -206,7 +206,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -245,7 +245,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) q := r.URL.Query() - q.Add(httpmw.SessionTokenKey, fmt.Sprintf("%s-%s", id, secret)) + q.Add(codersdk.SessionTokenKey, fmt.Sprintf("%s-%s", id, secret)) r.URL.RawQuery = q.Encode() _, err := db.InsertAPIKey(r.Context(), database.InsertAPIKeyParams{ @@ -278,7 +278,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -313,7 +313,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -348,7 +348,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -384,7 +384,7 @@ func TestAPIKey(t *testing.T) { user = createUser(r.Context(), t, db) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) @@ -433,7 +433,7 @@ func TestAPIKey(t *testing.T) { ) r.RemoteAddr = "1.1.1.1:3555" r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/authorize_test.go b/coderd/httpmw/authorize_test.go index 68feecbb7257f..997ac44350340 100644 --- a/coderd/httpmw/authorize_test.go +++ b/coderd/httpmw/authorize_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/coder/coder/coderd/rbac" + "github.com/coder/coder/codersdk" "github.com/google/uuid" @@ -93,7 +94,7 @@ func TestExtractUserRoles(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: token, }) diff --git a/coderd/httpmw/organizationparam_test.go b/coderd/httpmw/organizationparam_test.go index 9ffb3bdc57dfd..d17c441741914 100644 --- a/coderd/httpmw/organizationparam_test.go +++ b/coderd/httpmw/organizationparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestOrganizationParam(t *testing.T) { hashed = sha256.Sum256([]byte(secret)) ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/templateparam_test.go b/coderd/httpmw/templateparam_test.go index 5812882089c93..94abfe82cf5fb 100644 --- a/coderd/httpmw/templateparam_test.go +++ b/coderd/httpmw/templateparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestTemplateParam(t *testing.T) { ) r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/templateversionparam_test.go b/coderd/httpmw/templateversionparam_test.go index c60218c095b59..5b49f75010bf9 100644 --- a/coderd/httpmw/templateversionparam_test.go +++ b/coderd/httpmw/templateversionparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestTemplateVersionParam(t *testing.T) { ) r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/userparam_test.go b/coderd/httpmw/userparam_test.go index 4ffad787a12a7..866df68ef1eec 100644 --- a/coderd/httpmw/userparam_test.go +++ b/coderd/httpmw/userparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" ) func TestUserParam(t *testing.T) { @@ -29,7 +30,7 @@ func TestUserParam(t *testing.T) { rw = httptest.NewRecorder() ) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/workspaceagent.go b/coderd/httpmw/workspaceagent.go index 317c7b858e552..70331d80a540f 100644 --- a/coderd/httpmw/workspaceagent.go +++ b/coderd/httpmw/workspaceagent.go @@ -29,10 +29,10 @@ func WorkspaceAgent(r *http.Request) database.WorkspaceAgent { func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - cookie, err := r.Cookie(SessionTokenKey) + cookie, err := r.Cookie(codersdk.SessionTokenKey) if err != nil { httpapi.Write(rw, http.StatusUnauthorized, codersdk.Response{ - Message: fmt.Sprintf("Cookie %q must be provided.", SessionTokenKey), + Message: fmt.Sprintf("Cookie %q must be provided.", codersdk.SessionTokenKey), }) return } diff --git a/coderd/httpmw/workspaceagent_test.go b/coderd/httpmw/workspaceagent_test.go index 0661183abffcf..eef7fc80847f1 100644 --- a/coderd/httpmw/workspaceagent_test.go +++ b/coderd/httpmw/workspaceagent_test.go @@ -13,6 +13,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" ) func TestWorkspaceAgent(t *testing.T) { @@ -22,7 +23,7 @@ func TestWorkspaceAgent(t *testing.T) { token := uuid.New() r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: token.String(), }) return r, token diff --git a/coderd/httpmw/workspaceagentparam_test.go b/coderd/httpmw/workspaceagentparam_test.go index c79a10b3c3563..a2afaee534c9f 100644 --- a/coderd/httpmw/workspaceagentparam_test.go +++ b/coderd/httpmw/workspaceagentparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestWorkspaceAgentParam(t *testing.T) { ) r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/workspacebuildparam_test.go b/coderd/httpmw/workspacebuildparam_test.go index f6ceb332ceda7..6d402f01fc62b 100644 --- a/coderd/httpmw/workspacebuildparam_test.go +++ b/coderd/httpmw/workspacebuildparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestWorkspaceBuildParam(t *testing.T) { ) r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) diff --git a/coderd/httpmw/workspaceparam_test.go b/coderd/httpmw/workspaceparam_test.go index a1bfd63c1f9f8..eac847a584f3b 100644 --- a/coderd/httpmw/workspaceparam_test.go +++ b/coderd/httpmw/workspaceparam_test.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" ) @@ -29,7 +30,7 @@ func TestWorkspaceParam(t *testing.T) { ) r := httptest.NewRequest("GET", "/", nil) r.AddCookie(&http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: fmt.Sprintf("%s-%s", id, secret), }) From 7669d4b749ef8ca448d721376fe7e4ea03517669 Mon Sep 17 00:00:00 2001 From: sreya Date: Tue, 12 Jul 2022 21:48:37 +0000 Subject: [PATCH 3/6] some more cleanup --- coderd/httpapi/httpapi_test.go | 5 +++-- coderd/users.go | 4 ++-- coderd/users_test.go | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coderd/httpapi/httpapi_test.go b/coderd/httpapi/httpapi_test.go index 87f57babb3d6d..75f5878b5cbf8 100644 --- a/coderd/httpapi/httpapi_test.go +++ b/coderd/httpapi/httpapi_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/codersdk" ) func TestWrite(t *testing.T) { @@ -19,7 +20,7 @@ func TestWrite(t *testing.T) { t.Run("NoErrors", func(t *testing.T) { t.Parallel() rw := httptest.NewRecorder() - httpapi.Write(rw, http.StatusOK, httpapi.Response{ + httpapi.Write(rw, http.StatusOK, codersdk.Response{ Message: "Wow.", }) var m map[string]interface{} @@ -71,7 +72,7 @@ func TestRead(t *testing.T) { var validate toValidate require.False(t, httpapi.Read(rw, r, &validate)) - var v httpapi.Response + var v codersdk.Response err := json.NewDecoder(rw.Body).Decode(&v) require.NoError(t, err) require.Len(t, v.Validations, 1) diff --git a/coderd/users.go b/coderd/users.go index 91f9676b445b1..845998538983f 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -754,7 +754,7 @@ func (api *API) postLogout(rw http.ResponseWriter, r *http.Request) { cookie := &http.Cookie{ // MaxAge < 0 means to delete the cookie now. MaxAge: -1, - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Path: "/", } @@ -854,7 +854,7 @@ func (api *API) createAPIKey(rw http.ResponseWriter, r *http.Request, params dat // This format is consumed by the APIKey middleware. sessionToken := fmt.Sprintf("%s-%s", keyID, keySecret) http.SetCookie(rw, &http.Cookie{ - Name: httpmw.SessionTokenKey, + Name: codersdk.SessionTokenKey, Value: sessionToken, Path: "/", HttpOnly: true, diff --git a/coderd/users_test.go b/coderd/users_test.go index 2fe023fbb19c7..563dbdfc13664 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -13,7 +13,6 @@ import ( "github.com/stretchr/testify/require" "github.com/coder/coder/coderd/coderdtest" - "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/rbac" "github.com/coder/coder/codersdk" ) @@ -183,7 +182,7 @@ func TestPostLogout(t *testing.T) { cookies := res.Cookies() require.Len(t, cookies, 1, "Exactly one cookie should be returned") - require.Equal(t, httpmw.SessionTokenKey, cookies[0].Name, "Cookie should be the auth cookie") + require.Equal(t, codersdk.SessionTokenKey, cookies[0].Name, "Cookie should be the auth cookie") require.Equal(t, -1, cookies[0].MaxAge, "Cookie should be set to delete") _, err = client.GetAPIKey(ctx, admin.UserID.String(), keyID) From c7b4ce3ed7492f44fe380789476a47ee33157ea4 Mon Sep 17 00:00:00 2001 From: sreya Date: Tue, 12 Jul 2022 23:52:11 +0000 Subject: [PATCH 4/6] make gen --- site/src/api/typesGenerated.ts | 35 ++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 57f7cdba81800..74d2acd20d7ab 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -12,7 +12,7 @@ export interface APIKey { readonly lifetime_seconds: number } -// From codersdk/workspaceagents.go:36:6 +// From codersdk/workspaceagents.go:35:6 export interface AWSInstanceIdentityToken { readonly signature: string readonly document: string @@ -30,7 +30,7 @@ export interface AuthMethods { readonly github: boolean } -// From codersdk/workspaceagents.go:41:6 +// From codersdk/workspaceagents.go:40:6 export interface AzureInstanceIdentityToken { readonly signature: string readonly encoding: string @@ -141,11 +141,19 @@ export interface GitSSHKey { readonly public_key: string } -// From codersdk/workspaceagents.go:32:6 +// From codersdk/workspaceagents.go:31:6 export interface GoogleInstanceIdentityToken { readonly json_web_token: string } +// From codersdk/client.go:174:6 +export interface HTTPError extends Response { + readonly statusCode: number + readonly method: string + readonly url: string + readonly Helper: string +} + // From codersdk/users.go:154:6 export interface LoginWithPasswordRequest { readonly email: string @@ -214,7 +222,7 @@ export interface ParameterSchema { readonly validation_contains?: string[] } -// From codersdk/provisionerdaemons.go:38:6 +// From codersdk/provisionerdaemons.go:36:6 export interface ProvisionerDaemon { readonly id: string readonly created_at: string @@ -223,7 +231,7 @@ export interface ProvisionerDaemon { readonly provisioners: ProvisionerType[] } -// From codersdk/provisionerdaemons.go:67:6 +// From codersdk/provisionerdaemons.go:65:6 export interface ProvisionerJob { readonly id: string readonly created_at: string @@ -235,7 +243,7 @@ export interface ProvisionerJob { readonly storage_source: string } -// From codersdk/provisionerdaemons.go:78:6 +// From codersdk/provisionerdaemons.go:76:6 export interface ProvisionerJobLog { readonly id: string readonly created_at: string @@ -250,6 +258,13 @@ export interface PutExtendWorkspaceRequest { readonly deadline: string } +// From codersdk/error.go:4:6 +export interface Response { + readonly message: string + readonly detail?: string + readonly validations?: Error[] +} + // From codersdk/roles.go:12:6 export interface Role { readonly name: string @@ -422,7 +437,7 @@ export interface WorkspaceAgent { readonly ipv6: any } -// From codersdk/workspaceagents.go:48:6 +// From codersdk/workspaceagents.go:47:6 export interface WorkspaceAgentAuthenticateResponse { readonly session_token: string } @@ -506,10 +521,10 @@ export interface WorkspaceResource { // From codersdk/workspacebuilds.go:22:6 export type BuildReason = "autostart" | "autostop" | "initiator" -// From codersdk/provisionerdaemons.go:28:6 +// From codersdk/provisionerdaemons.go:26:6 export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" -// From codersdk/provisionerdaemons.go:21:6 +// From codersdk/provisionerdaemons.go:19:6 export type LogSource = "provisioner" | "provisioner_daemon" // From codersdk/users.go:25:6 @@ -527,7 +542,7 @@ export type ParameterSourceScheme = "data" | "none" // From codersdk/parameters.go:37:6 export type ParameterTypeSystem = "hcl" | "none" -// From codersdk/provisionerdaemons.go:47:6 +// From codersdk/provisionerdaemons.go:45:6 export type ProvisionerJobStatus = | "canceled" | "canceling" From a03ab1edb24aa728969d0940176b4bd44c1fba49 Mon Sep 17 00:00:00 2001 From: sreya Date: Wed, 13 Jul 2022 00:03:11 +0000 Subject: [PATCH 5/6] update some names --- cli/userlist_test.go | 2 +- coderd/coderdtest/coderdtest.go | 2 +- coderd/files_test.go | 2 +- coderd/httpapi/httpapi.go | 4 ++-- coderd/httpapi/queryparams.go | 12 +++++------ coderd/organizations_test.go | 6 +++--- coderd/pagination_internal_test.go | 2 +- coderd/parameters.go | 4 ++-- coderd/parameters_test.go | 6 +++--- coderd/provisionerjobs.go | 4 ++-- coderd/roles_test.go | 2 +- coderd/templates.go | 10 +++++----- coderd/templates_test.go | 12 +++++------ coderd/templateversions_test.go | 26 ++++++++++++------------ coderd/users.go | 12 +++++------ coderd/users_test.go | 24 +++++++++++----------- coderd/workspaceagents.go | 2 +- coderd/workspacebuilds.go | 2 +- coderd/workspacebuilds_test.go | 10 +++++----- coderd/workspaceresourceauth_test.go | 4 ++-- coderd/workspaces.go | 26 ++++++++++++------------ coderd/workspaces_test.go | 30 ++++++++++++++-------------- codersdk/client.go | 12 +++++------ codersdk/error.go | 6 +++--- 24 files changed, 111 insertions(+), 111 deletions(-) diff --git a/cli/userlist_test.go b/cli/userlist_test.go index fa5a15c79f370..c8b207a0a92fb 100644 --- a/cli/userlist_test.go +++ b/cli/userlist_test.go @@ -49,7 +49,7 @@ func TestUserList(t *testing.T) { _, err := cmd.ExecuteC() - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Contains(t, err.Error(), "Try logging in using 'coder login '.") }) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 28582c62c17d5..a6ace8eb20c38 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -278,7 +278,7 @@ func createAnotherUserRetry(t *testing.T, client *codersdk.Client, organizationI } user, err := client.CreateUser(context.Background(), req) - var apiError *codersdk.HTTPError + var apiError *codersdk.Error // If the user already exists by username or email conflict, try again up to "retries" times. if err != nil && retries >= 0 && xerrors.As(err, &apiError) { if apiError.StatusCode() == http.StatusConflict { diff --git a/coderd/files_test.go b/coderd/files_test.go index 57beac26c0c1a..016774a030c88 100644 --- a/coderd/files_test.go +++ b/coderd/files_test.go @@ -48,7 +48,7 @@ func TestDownload(t *testing.T) { client := coderdtest.New(t, nil) _ = coderdtest.CreateFirstUser(t, client) _, _, err := client.Download(context.Background(), "something") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/httpapi/httpapi.go b/coderd/httpapi/httpapi.go index f16840b958fba..2bfe281c2c760 100644 --- a/coderd/httpapi/httpapi.go +++ b/coderd/httpapi/httpapi.go @@ -99,9 +99,9 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool { err = validate.Struct(value) var validationErrors validator.ValidationErrors if errors.As(err, &validationErrors) { - apiErrors := make([]codersdk.Error, 0, len(validationErrors)) + apiErrors := make([]codersdk.ValidationError, 0, len(validationErrors)) for _, validationError := range validationErrors { - apiErrors = append(apiErrors, codersdk.Error{ + apiErrors = append(apiErrors, codersdk.ValidationError{ Field: validationError.Field(), Detail: fmt.Sprintf("Validation failed for tag %q with value: \"%v\"", validationError.Tag(), validationError.Value()), }) diff --git a/coderd/httpapi/queryparams.go b/coderd/httpapi/queryparams.go index a5f66b1f639bc..e1fe2c38f0b55 100644 --- a/coderd/httpapi/queryparams.go +++ b/coderd/httpapi/queryparams.go @@ -19,19 +19,19 @@ import ( type QueryParamParser struct { // Errors is the set of errors to return via the API. If the length // of this set is 0, there are no errors!. - Errors []codersdk.Error + Errors []codersdk.ValidationError } func NewQueryParamParser() *QueryParamParser { return &QueryParamParser{ - Errors: []codersdk.Error{}, + Errors: []codersdk.ValidationError{}, } } func (p *QueryParamParser) Int(vals url.Values, def int, queryParam string) int { v, err := parseQueryParam(vals, strconv.Atoi, def, queryParam) if err != nil { - p.Errors = append(p.Errors, codersdk.Error{ + p.Errors = append(p.Errors, codersdk.ValidationError{ Field: queryParam, Detail: fmt.Sprintf("Query param %q must be a valid integer (%s)", queryParam, err.Error()), }) @@ -49,7 +49,7 @@ func (p *QueryParamParser) UUIDorMe(vals url.Values, def uuid.UUID, me uuid.UUID func (p *QueryParamParser) UUID(vals url.Values, def uuid.UUID, queryParam string) uuid.UUID { v, err := parseQueryParam(vals, uuid.Parse, def, queryParam) if err != nil { - p.Errors = append(p.Errors, codersdk.Error{ + p.Errors = append(p.Errors, codersdk.ValidationError{ Field: queryParam, Detail: fmt.Sprintf("Query param %q must be a valid uuid", queryParam), }) @@ -77,7 +77,7 @@ func (p *QueryParamParser) UUIDs(vals url.Values, def []uuid.UUID, queryParam st return ids, nil }, def, queryParam) if err != nil { - p.Errors = append(p.Errors, codersdk.Error{ + p.Errors = append(p.Errors, codersdk.ValidationError{ Field: queryParam, Detail: fmt.Sprintf("Query param %q has invalid uuids: %q", queryParam, err.Error()), }) @@ -107,7 +107,7 @@ func (*QueryParamParser) Strings(vals url.Values, def []string, queryParam strin func ParseCustom[T any](parser *QueryParamParser, vals url.Values, def T, queryParam string, parseFunc func(v string) (T, error)) T { v, err := parseQueryParam(vals, parseFunc, def, queryParam) if err != nil { - parser.Errors = append(parser.Errors, codersdk.Error{ + parser.Errors = append(parser.Errors, codersdk.ValidationError{ Field: queryParam, Detail: fmt.Sprintf("Query param %q has invalid value: %s", queryParam, err.Error()), }) diff --git a/coderd/organizations_test.go b/coderd/organizations_test.go index 0e6fa7871755d..d329c65d59832 100644 --- a/coderd/organizations_test.go +++ b/coderd/organizations_test.go @@ -28,7 +28,7 @@ func TestOrganizationByUserAndName(t *testing.T) { client := coderdtest.New(t, nil) coderdtest.CreateFirstUser(t, client) _, err := client.OrganizationByName(context.Background(), codersdk.Me, "nothing") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -43,7 +43,7 @@ func TestOrganizationByUserAndName(t *testing.T) { }) require.NoError(t, err) _, err = other.OrganizationByName(context.Background(), codersdk.Me, org.Name) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -70,7 +70,7 @@ func TestPostOrganizationsByUser(t *testing.T) { _, err = client.CreateOrganization(context.Background(), codersdk.CreateOrganizationRequest{ Name: org.Name, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) diff --git a/coderd/pagination_internal_test.go b/coderd/pagination_internal_test.go index c37de9f6c54bd..978cfab417b2e 100644 --- a/coderd/pagination_internal_test.go +++ b/coderd/pagination_internal_test.go @@ -110,7 +110,7 @@ func TestPagination(t *testing.T) { } else { require.False(t, ok, "expect !ok") require.Equal(t, http.StatusBadRequest, rw.Code, "bad request status code") - var apiError codersdk.HTTPError + var apiError codersdk.Error err := json.NewDecoder(rw.Body).Decode(&apiError) require.NoError(t, err, "decode response") require.Contains(t, apiError.Message, c.ExpectedError, "expected error") diff --git a/coderd/parameters.go b/coderd/parameters.go index 016ce9fb7ee8d..7675e9ff9b1a8 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -245,7 +245,7 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter default: httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid scope %q.", scope), - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "scope", Detail: "invalid scope"}, }, }) @@ -258,7 +258,7 @@ func readScopeAndID(rw http.ResponseWriter, r *http.Request) (database.Parameter httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid UUID %q.", id), Detail: err.Error(), - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "id", Detail: "Invalid UUID"}, }, }) diff --git a/coderd/parameters_test.go b/coderd/parameters_test.go index b05084178c1d4..b0e519b938847 100644 --- a/coderd/parameters_test.go +++ b/coderd/parameters_test.go @@ -26,7 +26,7 @@ func TestPostParameter(t *testing.T) { SourceScheme: codersdk.ParameterSourceSchemeData, DestinationScheme: codersdk.ParameterDestinationSchemeProvisionerVariable, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -64,7 +64,7 @@ func TestPostParameter(t *testing.T) { SourceScheme: codersdk.ParameterSourceSchemeData, DestinationScheme: codersdk.ParameterDestinationSchemeProvisionerVariable, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -106,7 +106,7 @@ func TestDeleteParameter(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) template := createTemplate(t, client, user) err := client.DeleteParameter(context.Background(), codersdk.ParameterTemplate, template.ID, "something") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index bc435a890e81c..4ff2d6551c4e5 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -77,7 +77,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param \"after\" must be an integer.", - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "after", Detail: "Must be an integer"}, }, }) @@ -96,7 +96,7 @@ func (api *API) provisionerJobLogs(rw http.ResponseWriter, r *http.Request, job if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param \"before\" must be an integer.", - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "before", Detail: "Must be an integer"}, }, }) diff --git a/coderd/roles_test.go b/coderd/roles_test.go index d219dd6f4966e..a30daa46ba7a7 100644 --- a/coderd/roles_test.go +++ b/coderd/roles_test.go @@ -190,7 +190,7 @@ func TestListRoles(t *testing.T) { t.Parallel() roles, err := c.APICall() if c.AuthorizedError != "" { - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusForbidden, apiErr.StatusCode()) require.Contains(t, apiErr.Message, c.AuthorizedError) diff --git a/coderd/templates.go b/coderd/templates.go index d69f27035dff9..5eb2f5c8e218b 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -132,7 +132,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque if err == nil { httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: fmt.Sprintf("Template with name %q already exists.", createTemplate.Name), - Validations: []codersdk.Error{{ + Validations: []codersdk.ValidationError{{ Field: "name", Detail: "This value is already in use and should be unique.", }}, @@ -150,7 +150,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque if errors.Is(err, sql.ErrNoRows) { httpapi.Write(rw, http.StatusNotFound, codersdk.Response{ Message: fmt.Sprintf("Template version %q does not exist.", createTemplate.VersionID), - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "template_version_id", Detail: "Template version does not exist"}, }, }) @@ -369,12 +369,12 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { return } - var validErrs []codersdk.Error + var validErrs []codersdk.ValidationError if req.MaxTTLMillis < 0 { - validErrs = append(validErrs, codersdk.Error{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) + validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) } if req.MinAutostartIntervalMillis < 0 { - validErrs = append(validErrs, codersdk.Error{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."}) + validErrs = append(validErrs, codersdk.ValidationError{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."}) } if len(validErrs) > 0 { diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 60b9a87fbdacf..9f54d59003e9d 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -88,7 +88,7 @@ func TestPostTemplateByOrganization(t *testing.T) { Name: template.Name, VersionID: version.ID, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -101,7 +101,7 @@ func TestPostTemplateByOrganization(t *testing.T) { VersionID: uuid.New(), }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) require.Contains(t, err.Error(), "Try logging in using 'coder login '.") @@ -115,7 +115,7 @@ func TestPostTemplateByOrganization(t *testing.T) { Name: "test", VersionID: uuid.New(), }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -163,7 +163,7 @@ func TestTemplateByOrganizationAndName(t *testing.T) { client := coderdtest.New(t, nil) user := coderdtest.CreateFirstUser(t, client) _, err := client.TemplateByName(context.Background(), user.OrganizationID, "something") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -259,7 +259,7 @@ func TestPatchTemplateMeta(t *testing.T) { MinAutostartIntervalMillis: -int64(time.Hour), } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Contains(t, apiErr.Message, "Invalid request") require.Len(t, apiErr.Validations, 2) @@ -297,7 +297,7 @@ func TestDeleteTemplate(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) err := client.DeleteTemplate(context.Background(), template.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index d2e5759711354..a2cc37fc49931 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -41,7 +41,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { StorageSource: "hash", Provisioner: codersdk.ProvisionerTypeEcho, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -55,7 +55,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { StorageSource: "hash", Provisioner: codersdk.ProvisionerTypeEcho, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -96,7 +96,7 @@ func TestPatchCancelTemplateVersion(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) err := client.CancelTemplateVersion(context.Background(), version.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -122,7 +122,7 @@ func TestPatchCancelTemplateVersion(t *testing.T) { err := client.CancelTemplateVersion(context.Background(), version.ID) require.NoError(t, err) err = client.CancelTemplateVersion(context.Background(), version.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -164,7 +164,7 @@ func TestTemplateVersionSchema(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionSchema(context.Background(), version.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -232,7 +232,7 @@ func TestTemplateVersionParameters(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionParameters(context.Background(), version.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -277,7 +277,7 @@ func TestTemplateVersionResources(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) _, err := client.TemplateVersionResources(context.Background(), version.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -388,7 +388,7 @@ func TestTemplateVersionByName(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) _, err := client.TemplateVersionByName(context.Background(), template.ID, "nothing") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -415,7 +415,7 @@ func TestPatchActiveTemplateVersion(t *testing.T) { err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: uuid.New(), }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -430,7 +430,7 @@ func TestPatchActiveTemplateVersion(t *testing.T) { err := client.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: version.ID, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -543,7 +543,7 @@ func TestTemplateVersionDryRun(t *testing.T) { _, err := client.CreateTemplateVersionDryRun(context.Background(), version.ID, codersdk.CreateTemplateVersionDryRunRequest{ ParameterValues: []codersdk.CreateParameterRequest{}, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -625,7 +625,7 @@ func TestTemplateVersionDryRun(t *testing.T) { }, 5*time.Second, 25*time.Millisecond) err = client.CancelTemplateVersionDryRun(context.Background(), version.ID, job.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -666,7 +666,7 @@ func TestTemplateVersionDryRun(t *testing.T) { require.NoError(t, err) err = client.CancelTemplateVersionDryRun(context.Background(), version.ID, job.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/users.go b/coderd/users.go index 845998538983f..c41cdee03a1fd 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -292,9 +292,9 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) { isDifferentUser := existentUser.ID != user.ID if err == nil && isDifferentUser { - responseErrors := []codersdk.Error{} + responseErrors := []codersdk.ValidationError{} if existentUser.Username == params.Username { - responseErrors = append(responseErrors, codersdk.Error{ + responseErrors = append(responseErrors, codersdk.ValidationError{ Field: "username", Detail: "this value is already in use and should be unique", }) @@ -403,7 +403,7 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid password.", - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ { Field: "password", Detail: err.Error(), @@ -432,7 +432,7 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { if !ok { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Old password is incorrect.", - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ { Field: "old_password", Detail: "Old password is incorrect.", @@ -985,7 +985,7 @@ func findUser(id uuid.UUID, users []database.User) *database.User { return nil } -func userSearchQuery(query string) (database.GetUsersParams, []codersdk.Error) { +func userSearchQuery(query string) (database.GetUsersParams, []codersdk.ValidationError) { searchParams := make(url.Values) if query == "" { // No filter @@ -1005,7 +1005,7 @@ func userSearchQuery(query string) (database.GetUsersParams, []codersdk.Error) { case 2: searchParams.Set(parts[0], parts[1]) default: - return database.GetUsersParams{}, []codersdk.Error{ + return database.GetUsersParams{}, []codersdk.ValidationError{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 ':'", element)}, } } diff --git a/coderd/users_test.go b/coderd/users_test.go index 563dbdfc13664..72c3ba2f99420 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -36,7 +36,7 @@ func TestFirstUser(t *testing.T) { Password: "password", OrganizationName: "someorg", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -57,7 +57,7 @@ func TestPostLogin(t *testing.T) { Email: "my@email.org", Password: "password", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -77,7 +77,7 @@ func TestPostLogin(t *testing.T) { Email: req.Email, Password: "badpass", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -96,7 +96,7 @@ func TestPostLogin(t *testing.T) { // Test an existing session _, err = member.User(context.Background(), codersdk.Me) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) require.Contains(t, apiErr.Message, "Contact an admin") @@ -186,7 +186,7 @@ func TestPostLogout(t *testing.T) { require.Equal(t, -1, cookies[0].MaxAge, "Cookie should be set to delete") _, err = client.GetAPIKey(ctx, admin.UserID.String(), keyID) - var sdkErr = &codersdk.HTTPError{} + var sdkErr = &codersdk.Error{} require.ErrorAs(t, err, &sdkErr) require.Equal(t, http.StatusUnauthorized, sdkErr.StatusCode(), "Expecting 401") }) @@ -213,7 +213,7 @@ func TestPostUsers(t *testing.T) { Password: "password", OrganizationID: uuid.New(), }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -228,7 +228,7 @@ func TestPostUsers(t *testing.T) { Username: "someone-else", Password: "testing", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -250,7 +250,7 @@ func TestPostUsers(t *testing.T) { Password: "testing", OrganizationID: org.ID, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusForbidden, apiErr.StatusCode()) }) @@ -278,7 +278,7 @@ func TestUpdateUserProfile(t *testing.T) { _, err := client.UpdateUserProfile(context.Background(), uuid.New().String(), codersdk.UpdateUserProfileRequest{ Username: "newusername", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) // Right now, we are raising a BAD request error because we don't support a // user accessing other users info @@ -299,7 +299,7 @@ func TestUpdateUserProfile(t *testing.T) { _, err = client.UpdateUserProfile(context.Background(), codersdk.Me, codersdk.UpdateUserProfileRequest{ Username: existentUser.Username, }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -390,7 +390,7 @@ func TestGrantRoles(t *testing.T) { requireStatusCode := func(t *testing.T, err error, statusCode int) { t.Helper() - var e *codersdk.HTTPError + var e *codersdk.Error require.ErrorAs(t, err, &e, "error is codersdk error") require.Equal(t, statusCode, e.StatusCode(), "correct status code") } @@ -843,7 +843,7 @@ func TestPostAPIKey(t *testing.T) { client.SessionToken = "" _, err := client.CreateAPIKey(context.Background(), codersdk.Me) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index fca8fae262d5b..42b965fd64b67 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -421,7 +421,7 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Query param 'reconnect' must be a valid UUID.", - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "reconnect", Detail: "invalid UUID"}, }, }) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index c43911ffd1795..5740ab4b6b5fd 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -324,7 +324,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { if errors.Is(err, sql.ErrNoRows) { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Template version not found.", - Validations: []codersdk.Error{{ + Validations: []codersdk.ValidationError{{ Field: "template_version_id", Detail: "template version not found", }}, diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index 955c860af3577..9ee40a6460525 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -67,7 +67,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { workspace.Name, "buildNumber", ) - var apiError *codersdk.HTTPError + var apiError *codersdk.Error require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusBadRequest, apiError.StatusCode()) require.ErrorContains(t, apiError, "Failed to parse build number as integer.") @@ -89,7 +89,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { "workspaceName", strconv.FormatInt(int64(workspace.LatestBuild.BuildNumber), 10), ) - var apiError *codersdk.HTTPError + var apiError *codersdk.Error require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusNotFound, apiError.StatusCode()) require.ErrorContains(t, apiError, "Resource not found") @@ -111,7 +111,7 @@ func TestWorkspaceBuildByBuildNumber(t *testing.T) { workspace.Name, "200", ) - var apiError *codersdk.HTTPError + var apiError *codersdk.Error require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusNotFound, apiError.StatusCode()) require.ErrorContains(t, apiError, fmt.Sprintf("Workspace %q Build 200 does not exist.", workspace.Name)) @@ -156,7 +156,7 @@ func TestWorkspaceBuilds(t *testing.T) { AfterID: uuid.New(), }, }) - var apiError *codersdk.HTTPError + var apiError *codersdk.Error require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusBadRequest, apiError.StatusCode()) require.Contains(t, apiError.Message, "does not exist") @@ -246,7 +246,7 @@ func TestWorkspaceBuildResources(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _, err := client.WorkspaceResourcesByBuild(context.Background(), workspace.LatestBuild.ID) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) diff --git a/coderd/workspaceresourceauth_test.go b/coderd/workspaceresourceauth_test.go index 6cf6b3ab6d2a7..04374c1821dc4 100644 --- a/coderd/workspaceresourceauth_test.go +++ b/coderd/workspaceresourceauth_test.go @@ -100,7 +100,7 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { GoogleTokenValidator: validator, }) _, err := client.AuthWorkspaceGoogleInstanceIdentity(context.Background(), "", metadata) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -113,7 +113,7 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { GoogleTokenValidator: validator, }) _, err := client.AuthWorkspaceGoogleInstanceIdentity(context.Background(), "", metadata) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 58f88cbd5fea3..afc2152841ce7 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -51,7 +51,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", deletedStr), - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "deleted", Detail: "Must be a valid boolean"}, }, }) @@ -158,7 +158,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Invalid boolean value %q for \"include_deleted\" query param.", s), - Validations: []codersdk.Error{ + Validations: []codersdk.ValidationError{ {Field: "include_deleted", Detail: "Must be a valid boolean"}, }, }) @@ -249,7 +249,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req if errors.Is(err, sql.ErrNoRows) { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: fmt.Sprintf("Template %q doesn't exist.", createWorkspace.TemplateID.String()), - Validations: []codersdk.Error{{ + Validations: []codersdk.ValidationError{{ Field: "template_id", Detail: "template not found", }}, @@ -280,7 +280,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Autostart Schedule.", - Validations: []codersdk.Error{{Field: "schedule", Detail: err.Error()}}, + Validations: []codersdk.ValidationError{{Field: "schedule", Detail: err.Error()}}, }) return } @@ -289,7 +289,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace TTL.", - Validations: []codersdk.Error{{Field: "ttl_ms", Detail: err.Error()}}, + Validations: []codersdk.ValidationError{{Field: "ttl_ms", Detail: err.Error()}}, }) return } @@ -316,7 +316,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req // The template is fetched for clarity to the user on where the conflicting name may be. httpapi.Write(rw, http.StatusConflict, codersdk.Response{ Message: fmt.Sprintf("Workspace %q already exists in the %q template.", createWorkspace.Name, template.Name), - Validations: []codersdk.Error{{ + Validations: []codersdk.ValidationError{{ Field: "name", Detail: "this value is already in use and should be unique", }}, @@ -493,7 +493,7 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { if err != nil { httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid autostart schedule.", - Validations: []codersdk.Error{{Field: "schedule", Detail: err.Error()}}, + Validations: []codersdk.ValidationError{{Field: "schedule", Detail: err.Error()}}, }) return } @@ -523,7 +523,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { return } - var validErrs []codersdk.Error + var validErrs []codersdk.ValidationError err := api.Database.InTx(func(s database.Store) error { template, err := s.GetTemplateByID(r.Context(), workspace.TemplateID) @@ -536,7 +536,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { dbTTL, err := validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.MaxTtl)) if err != nil { - validErrs = append(validErrs, codersdk.Error{Field: "ttl_ms", Detail: err.Error()}) + validErrs = append(validErrs, codersdk.ValidationError{Field: "ttl_ms", Detail: err.Error()}) return err } if err := s.UpdateWorkspaceTTL(r.Context(), database.UpdateWorkspaceTTLParams{ @@ -625,7 +625,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline, time.Duration(template.MaxTtl)); err != nil { code = http.StatusBadRequest resp.Message = "Bad extend workspace request." - resp.Validations = append(resp.Validations, codersdk.Error{Field: "deadline", Detail: err.Error()}) + resp.Validations = append(resp.Validations, codersdk.ValidationError{Field: "deadline", Detail: err.Error()}) return err } @@ -953,7 +953,7 @@ func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error // workspaceSearchQuery takes a query string and returns the workspace filter. // It also can return the list of validation errors to return to the api. -func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersdk.Error) { +func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersdk.ValidationError) { searchParams := make(url.Values) if query == "" { // No filter @@ -977,14 +977,14 @@ func workspaceSearchQuery(query string) (database.GetWorkspacesParams, []codersd searchParams.Set("owner", parts[0]) searchParams.Set("name", parts[1]) default: - return database.GetWorkspacesParams{}, []codersdk.Error{ + return database.GetWorkspacesParams{}, []codersdk.ValidationError{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 '/'", element)}, } } case 2: searchParams.Set(parts[0], parts[1]) default: - return database.GetWorkspacesParams{}, []codersdk.Error{ + return database.GetWorkspacesParams{}, []codersdk.ValidationError{ {Field: "q", Detail: fmt.Sprintf("Query element %q can only contain 1 ':'", element)}, } } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 90d13854a8e16..2d607a5e3fa0f 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -112,7 +112,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: "workspace", }) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -135,7 +135,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: "workspace", }) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -153,7 +153,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { Name: workspace.Name, }) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -202,7 +202,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -224,7 +224,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -247,7 +247,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { } _, err := client.CreateWorkspace(context.Background(), template.OrganizationID, req) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) @@ -262,7 +262,7 @@ func TestWorkspaceByOwnerAndName(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) _, err := client.WorkspaceByOwnerAndName(context.Background(), codersdk.Me, "something", codersdk.WorkspaceOptions{}) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) }) @@ -634,7 +634,7 @@ func TestPostWorkspaceBuild(t *testing.T) { Transition: codersdk.WorkspaceTransitionStart, }) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) @@ -652,7 +652,7 @@ func TestPostWorkspaceBuild(t *testing.T) { TemplateID: template.ID, Name: "workspace", }) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) }) @@ -674,7 +674,7 @@ func TestPostWorkspaceBuild(t *testing.T) { Transition: codersdk.WorkspaceTransitionStart, }) require.Error(t, err) - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusConflict, apiErr.StatusCode()) }) @@ -754,7 +754,7 @@ func TestWorkspaceBuildByName(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _, err := client.WorkspaceBuildByName(context.Background(), workspace.ID, "something") - var apiErr *codersdk.HTTPError + var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) @@ -905,8 +905,8 @@ func TestWorkspaceUpdateAutostart(t *testing.T) { ) err := client.UpdateWorkspaceAutostart(ctx, wsid, req) - require.IsType(t, err, &codersdk.HTTPError{}, "expected codersdk.Error") - coderSDKErr, _ := err.(*codersdk.HTTPError) //nolint:errorlint + 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.Contains(t, coderSDKErr.Message, "Resource not found", "unexpected response code") }) @@ -1012,8 +1012,8 @@ func TestWorkspaceUpdateTTL(t *testing.T) { ) err := client.UpdateWorkspaceTTL(ctx, wsid, req) - require.IsType(t, err, &codersdk.HTTPError{}, "expected codersdk.Error") - coderSDKErr, _ := err.(*codersdk.HTTPError) //nolint:errorlint + 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.Contains(t, coderSDKErr.Message, "Resource not found", "unexpected response code") }) diff --git a/codersdk/client.go b/codersdk/client.go index 36693540a7d3c..06185bdbd7570 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -138,7 +138,7 @@ func readBodyAsError(res *http.Response) error { if err != nil { return xerrors.Errorf("read body: %w", err) } - return &HTTPError{ + return &Error{ statusCode: res.StatusCode, Response: Response{ Message: string(resp), @@ -153,14 +153,14 @@ func readBodyAsError(res *http.Response) error { if err != nil { if errors.Is(err, io.EOF) { // If no body is sent, we'll just provide the status code. - return &HTTPError{ + return &Error{ statusCode: res.StatusCode, Helper: helper, } } return xerrors.Errorf("decode body: %w", err) } - return &HTTPError{ + return &Error{ Response: m, statusCode: res.StatusCode, method: method, @@ -171,7 +171,7 @@ func readBodyAsError(res *http.Response) error { // Error represents an unaccepted or invalid request to the API. // @typescript-ignore Error -type HTTPError struct { +type Error struct { Response statusCode int @@ -181,11 +181,11 @@ type HTTPError struct { Helper string } -func (e *HTTPError) StatusCode() int { +func (e *Error) StatusCode() int { return e.statusCode } -func (e *HTTPError) Error() string { +func (e *Error) Error() string { var builder strings.Builder if e.method != "" && e.url != "" { _, _ = fmt.Fprintf(&builder, "%v %v: ", e.method, e.url) diff --git a/codersdk/error.go b/codersdk/error.go index 5de2977838e86..1dd16f98cb506 100644 --- a/codersdk/error.go +++ b/codersdk/error.go @@ -17,11 +17,11 @@ type Response struct { // 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:"validations,omitempty"` + Validations []ValidationError `json:"validations,omitempty"` } -// Error represents a scoped error to a user input. -type Error struct { +// ValidationError represents a scoped error to a user input. +type ValidationError struct { Field string `json:"field" validate:"required"` Detail string `json:"detail" validate:"required"` } From 54c1fdbe30da3ee912980fce0be68a8088d8d9e6 Mon Sep 17 00:00:00 2001 From: sreya Date: Wed, 13 Jul 2022 00:03:28 +0000 Subject: [PATCH 6/6] make gen again --- site/src/api/typesGenerated.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 74d2acd20d7ab..be3acf0a7ca98 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -146,14 +146,6 @@ export interface GoogleInstanceIdentityToken { readonly json_web_token: string } -// From codersdk/client.go:174:6 -export interface HTTPError extends Response { - readonly statusCode: number - readonly method: string - readonly url: string - readonly Helper: string -} - // From codersdk/users.go:154:6 export interface LoginWithPasswordRequest { readonly email: string @@ -262,7 +254,7 @@ export interface PutExtendWorkspaceRequest { export interface Response { readonly message: string readonly detail?: string - readonly validations?: Error[] + readonly validations?: ValidationError[] } // From codersdk/roles.go:12:6 @@ -392,6 +384,12 @@ export interface UsersRequest extends Pagination { readonly q?: string } +// From codersdk/error.go:24:6 +export interface ValidationError { + readonly field: string + readonly detail: string +} + // From codersdk/workspaces.go:19:6 export interface Workspace { readonly id: string