Improve error handling to match go standards - don't wrap and rethrow, but log and return. Also adds some more validations for path creation and resource moving. Add accesslogging for git and api router (#14)
This commit contains the following: - Improve and simplify error handling (remove unnecessary wrappers, make it feel like go) - Add extra validation for path creation and resource moving (path has to be within same top space, no top space alias allowed) - Add access logging for rest api and git api
This commit is contained in:
parent
4812beedc6
commit
b7b9f53b0d
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package comms
|
||||
|
||||
const (
|
||||
Internal = "Internal error occured - Please contact operator for more information."
|
||||
AuthenticationRequired = "Action requires authentication."
|
||||
)
|
|
@ -8,17 +8,20 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotAuthenticated = errors.New("Not authenticated.")
|
||||
ErrNotAuthorized = errors.New("Not authorized.")
|
||||
)
|
||||
|
||||
type Guard struct {
|
||||
authorizer authz.Authorizer
|
||||
}
|
||||
|
@ -35,12 +38,12 @@ func (g *Guard) EnforceAdmin(next http.Handler) http.Handler {
|
|||
ctx := r.Context()
|
||||
user, ok := request.UserFrom(ctx)
|
||||
if !ok {
|
||||
render.Unauthorizedf(w, comms.AuthenticationRequired)
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.Admin {
|
||||
render.Forbiddenf(w, "Action requires admin privileges.")
|
||||
render.Forbidden(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -56,7 +59,7 @@ func (g *Guard) EnforceAuthenticated(next http.Handler) http.Handler {
|
|||
ctx := r.Context()
|
||||
_, ok := request.UserFrom(ctx)
|
||||
if !ok {
|
||||
render.Unauthorizedf(w, comms.AuthenticationRequired)
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -73,19 +76,21 @@ func (g *Guard) Enforce(w http.ResponseWriter, r *http.Request, scope *types.Sco
|
|||
err := g.Check(r, scope, resource, permission)
|
||||
|
||||
// render error if needed
|
||||
if errors.Is(err, errs.NotAuthenticated) {
|
||||
render.Unauthorizedf(w, comms.AuthenticationRequired)
|
||||
} else if errors.Is(err, errs.NotAuthorized) {
|
||||
render.Forbiddenf(w, "User not authorized to perform %s on resource %v in scope %v",
|
||||
if errors.Is(err, ErrNotAuthenticated) {
|
||||
render.ErrorObject(w, http.StatusUnauthorized, render.ErrUnauthorized)
|
||||
} else if errors.Is(err, ErrNotAuthorized) {
|
||||
// log error for debugging.
|
||||
hlog.FromRequest(r).Debug().Msgf("User not authorized to perform %s on resource %v in scope %v",
|
||||
permission,
|
||||
resource,
|
||||
scope)
|
||||
|
||||
render.Forbidden(w)
|
||||
} else if err != nil {
|
||||
// log err for debugging
|
||||
log := hlog.FromRequest(r)
|
||||
log.Err(err).Msg("Encountered unexpected error while enforcing permission.")
|
||||
hlog.FromRequest(r).Err(err).Msg("Encountered unexpected error while enforcing permission.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
}
|
||||
|
||||
return err == nil
|
||||
|
@ -99,7 +104,7 @@ func (g *Guard) Enforce(w http.ResponseWriter, r *http.Request, scope *types.Sco
|
|||
func (g *Guard) Check(r *http.Request, scope *types.Scope, resource *types.Resource, permission enum.Permission) error {
|
||||
u, present := request.UserFrom(r.Context())
|
||||
if !present {
|
||||
return errs.NotAuthenticated
|
||||
return ErrNotAuthenticated
|
||||
}
|
||||
|
||||
// TODO: don't hardcode principal type USER
|
||||
|
@ -110,11 +115,11 @@ func (g *Guard) Check(r *http.Request, scope *types.Scope, resource *types.Resou
|
|||
resource,
|
||||
permission)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Authorization check failed")
|
||||
return err
|
||||
}
|
||||
|
||||
if !authorized {
|
||||
return errs.NotAuthorized
|
||||
return ErrNotAuthorized
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -7,7 +7,6 @@ package guard
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
|
@ -15,7 +14,6 @@ import (
|
|||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -44,11 +42,9 @@ func (g *Guard) Repo(permission enum.Permission, orPublic bool, guarded http.Han
|
|||
rep, ok := request.RepoFrom(ctx)
|
||||
if !ok {
|
||||
// log error for debugging
|
||||
log := hlog.FromRequest(r)
|
||||
log.Error().Msg("Method expects the repository to be availabe in the request context, but it wasnt.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
hlog.FromRequest(r).Error().Msg("Method expects the repository to be availabe in the request context, but it wasnt.")
|
||||
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -70,10 +66,9 @@ func (g *Guard) EnforceRepo(w http.ResponseWriter, r *http.Request, permission e
|
|||
spacePath, name, err := paths.Disect(path)
|
||||
if err != nil {
|
||||
// log error for debugging
|
||||
hlog.FromRequest(r)
|
||||
log.Err(err).Msgf("Failed to disect path '%s'.", path)
|
||||
hlog.FromRequest(r).Err(err).Msgf("Failed to disect path '%s'.", path)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ package guard
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
|
@ -43,7 +42,10 @@ func (g *Guard) Space(permission enum.Permission, orPublic bool, guarded http.Ha
|
|||
ctx := r.Context()
|
||||
s, ok := request.SpaceFrom(ctx)
|
||||
if !ok {
|
||||
render.InternalError(w, errors.New("Expected space to be available"))
|
||||
// log error for debugging
|
||||
hlog.FromRequest(r).Error().Msg("Method expects the space to be availabe in the request context, but it wasnt.")
|
||||
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -68,7 +70,7 @@ func (g *Guard) EnforceSpace(w http.ResponseWriter, r *http.Request, permission
|
|||
hlog.FromRequest(r)
|
||||
log.Err(err).Msgf("Failed to disect path '%s'.", path)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ func HandleLogin(users store.UserStore, system store.SystemStore) http.HandlerFu
|
|||
Msg("cannot find user")
|
||||
|
||||
// always give not found error as extra security measurement.
|
||||
render.NotFoundf(w, "Invalid email or password")
|
||||
render.NotFound(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ func HandleLogin(users store.UserStore, system store.SystemStore) http.HandlerFu
|
|||
Str("user", username).
|
||||
Msg("invalid password")
|
||||
|
||||
render.NotFoundf(w, "Invalid email or password")
|
||||
render.NotFound(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -57,23 +57,24 @@ func HandleLogin(users store.UserStore, system store.SystemStore) http.HandlerFu
|
|||
Str("user", username).
|
||||
Msg("failed to generate token")
|
||||
|
||||
render.InternalErrorf(w, "Failed to create session")
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// return the token if the with_user boolean
|
||||
// query parameter is set to true.
|
||||
if r.FormValue("return_user") == "true" {
|
||||
render.JSON(w, &types.UserToken{
|
||||
User: user,
|
||||
Token: &types.Token{
|
||||
Value: token_,
|
||||
Expires: expires.UTC(),
|
||||
},
|
||||
}, 200)
|
||||
render.JSON(w, http.StatusOK,
|
||||
&types.UserToken{
|
||||
User: user,
|
||||
Token: &types.Token{
|
||||
Value: token_,
|
||||
Expires: expires.UTC(),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// else return the token only.
|
||||
render.JSON(w, &types.Token{Value: token_}, 200)
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token_})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/internal/token"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -37,7 +35,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Str("email", username).
|
||||
Msg("Failed to hash password")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -56,7 +54,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Str("email", username).
|
||||
Msg("invalid user input")
|
||||
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -65,7 +63,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Str("email", username).
|
||||
Msg("Failed to create user")
|
||||
|
||||
render.InternalError(w, errs.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -80,7 +78,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Int64("user_id", user.ID).
|
||||
Msg("Failed to enable admin user")
|
||||
|
||||
render.InternalError(w, errs.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -93,23 +91,23 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Int64("user_id", user.ID).
|
||||
Msg("Failed to generate token")
|
||||
|
||||
render.InternalError(w, errs.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// return the token if the with_user boolean
|
||||
// query parameter is set to true.
|
||||
if r.FormValue("return_user") == "true" {
|
||||
render.JSON(w, &types.UserToken{
|
||||
render.JSON(w, http.StatusOK, &types.UserToken{
|
||||
User: user,
|
||||
Token: &types.Token{
|
||||
Value: token_,
|
||||
Expires: expires.UTC(),
|
||||
},
|
||||
}, 200)
|
||||
})
|
||||
} else {
|
||||
// else return the token only.
|
||||
render.JSON(w, &types.Token{Value: token_}, 200)
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token_})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,13 @@ package repo
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
@ -58,13 +54,10 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore, repos store.RepoS
|
|||
}
|
||||
|
||||
parentSpace, err := spaces.Find(ctx, in.SpaceId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Provided space wasn't found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%s'.", in.SpaceId)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.SpaceId)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -102,26 +95,20 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore, repos store.RepoS
|
|||
|
||||
// validate repo
|
||||
if err := check.Repo(repo); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// create in store
|
||||
err = repos.Create(ctx, repo)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Repository creation failed as a duplicate was detected.")
|
||||
|
||||
render.BadRequestf(w, "Path '%s' already exists.", paths.Concatinate(parentPath, repo.Name))
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Repository creation failed.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, repo, 200)
|
||||
render.JSON(w, http.StatusOK, repo)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,14 @@ package repo
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/handler/common"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
@ -52,27 +49,21 @@ func HandleCreatePath(guard *guard.Guard, repos store.RepoStore) http.HandlerFun
|
|||
}
|
||||
|
||||
// validate path
|
||||
if err = check.PathParams(params.Path, false); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
if err = check.PathParams(params, repo.Path, false); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: ensure user is authorized to create a path pointing to in.Path
|
||||
path, err := repos.CreatePath(ctx, repo.ID, params)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to create path for repo as a duplicate was detected.")
|
||||
|
||||
render.BadRequestf(w, "Path '%s' already exists.", params.Path)
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Failed to create path for repo.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, path, 200)
|
||||
render.JSON(w, http.StatusOK, path)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
@ -31,13 +28,10 @@ func HandleDelete(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc {
|
|||
repo, _ := request.RepoFrom(ctx)
|
||||
|
||||
err := repos.Delete(r.Context(), repo.ID)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Repository doesn't exist.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to delete the Repository.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
@ -32,23 +29,16 @@ func HandleDeletePath(guard *guard.Guard, repos store.RepoStore) http.HandlerFun
|
|||
|
||||
pathId, err := request.GetPathId(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
err = repos.DeletePath(ctx, repo.ID, pathId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Path doesn't exist.")
|
||||
return
|
||||
} else if errors.Is(err, errs.PrimaryPathCantBeDeleted) {
|
||||
render.BadRequestf(w, "Deleting a primary path is not allowed.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).Int64("path_id", pathId).
|
||||
Msgf("Failed to delete repo path.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
return
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
|
|
@ -25,6 +25,6 @@ func HandleFind(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc {
|
|||
ctx := r.Context()
|
||||
repo, _ := request.RepoFrom(ctx)
|
||||
|
||||
render.JSON(w, repo, 200)
|
||||
render.JSON(w, http.StatusOK, repo)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package repo
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -37,10 +36,11 @@ func HandleListPaths(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get list of repo paths.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, paths, 200)
|
||||
// TODO: do we need pagination? we should block that many paths in the first place.
|
||||
render.JSON(w, http.StatusOK, paths)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,15 +6,12 @@ package repo
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
@ -61,27 +58,23 @@ func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceSto
|
|||
|
||||
// ensure we don't end up in any missconfiguration, and block no-ops
|
||||
if err = check.Name(*in.Name); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
} else if *in.SpaceId == repo.SpaceId && *in.Name == repo.Name {
|
||||
render.BadRequest(w, errs.NoChangeInRequestedMove)
|
||||
render.BadRequestError(w, render.ErrNoChange)
|
||||
return
|
||||
} else if *in.SpaceId <= 0 {
|
||||
render.BadRequest(w, check.ErrRepositoryRequiresSpaceId)
|
||||
render.UserfiedErrorOrInternal(w, check.ErrRepositoryRequiresSpaceId)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we have access to the target space (if its a space move)
|
||||
if *in.SpaceId != repo.SpaceId {
|
||||
newSpace, err := spaces.Find(ctx, *in.SpaceId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Parent space not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).
|
||||
Msgf("Failed to get target space with id %d for the move.", *in.SpaceId)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get target space with id %d for the move.", *in.SpaceId)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -97,20 +90,13 @@ func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceSto
|
|||
}
|
||||
|
||||
res, err := repos.Move(ctx, usr.ID, repo.ID, *in.SpaceId, *in.Name, in.KeepAsAlias)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to move the repo as a duplicate was detected.")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to move the repository.")
|
||||
|
||||
render.BadRequestf(w, "Unable to move the repository as the destination path is already taken.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Failed to move the repository.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, res, http.StatusOK)
|
||||
render.JSON(w, http.StatusOK, res)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -59,19 +58,18 @@ func HandleUpdate(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc {
|
|||
|
||||
// ensure provided values are valid
|
||||
if err := check.Repo(repo); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = repos.Update(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Repository update failed.")
|
||||
log.Error().Err(err).Msg("Repository update failed.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, repo, http.StatusOK)
|
||||
render.JSON(w, http.StatusOK, repo)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,16 +6,13 @@ package space
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -60,19 +57,16 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
if in.ParentId <= 0 {
|
||||
// TODO: Restrict top level space creation.
|
||||
if usr == nil {
|
||||
render.Unauthorizedf(w, comms.AuthenticationRequired)
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Create is a special case - we need the parent path
|
||||
parent, err := spaces.Find(ctx, in.ParentId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Provided parent space wasn't found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%s'.", in.ParentId)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.ParentId)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -102,39 +96,27 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
|
||||
// validate space
|
||||
if err := check.Space(space); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate path (Due to racing conditions we can't be 100% sure on the path here only best effort to store failure)
|
||||
// Validate path length (Due to racing conditions we can't be 100% sure on the path here only best effort to have a quick failure)
|
||||
path := paths.Concatinate(parentPath, space.Name)
|
||||
if err = check.PathParams(path, true); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
if err = check.Path(path, true); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// create in store
|
||||
err = spaces.Create(ctx, space)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Space creation failed as a duplicate was detected.")
|
||||
|
||||
render.BadRequestf(w, "Path '%s' already exists.", path)
|
||||
return
|
||||
} else if errors.Is(err, errs.PathTooLong) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to move the space as its path was too long.")
|
||||
|
||||
render.BadRequestf(w, "Unable to move the space as the destination path of the space was too long.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Space creation failed.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, space, 200)
|
||||
render.JSON(w, http.StatusOK, space)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,14 @@ package space
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/handler/common"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
@ -52,27 +49,20 @@ func HandleCreatePath(guard *guard.Guard, spaces store.SpaceStore) http.HandlerF
|
|||
}
|
||||
|
||||
// validate path
|
||||
if err = check.PathParams(params.Path, true); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
if err = check.PathParams(params, space.Path, true); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: ensure user is authorized to create a path pointing to in.Path
|
||||
path, err := spaces.CreatePath(ctx, space.ID, params)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to create path for space as a duplicate was detected.")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to create path for space.")
|
||||
|
||||
render.BadRequestf(w, "Path '%s' already exists.", params.Path)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Failed to create path for space.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, path, 200)
|
||||
render.JSON(w, http.StatusOK, path)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
@ -31,13 +28,10 @@ func HandleDelete(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
s, _ := request.SpaceFrom(ctx)
|
||||
|
||||
err := spaces.Delete(r.Context(), s.ID)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Space not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to delete the space.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
@ -32,22 +29,16 @@ func HandleDeletePath(guard *guard.Guard, spaces store.SpaceStore) http.HandlerF
|
|||
|
||||
pathId, err := request.GetPathId(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
err = spaces.DeletePath(ctx, space.ID, pathId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Path doesn't exist.")
|
||||
return
|
||||
} else if errors.Is(err, errs.PrimaryPathCantBeDeleted) {
|
||||
render.BadRequestf(w, "Deleting a primary path is not allowed.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).Int64("path_id", pathId).
|
||||
Msgf("Failed to delete space path.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,6 @@ func HandleFind(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
ctx := r.Context()
|
||||
s, _ := request.SpaceFrom(ctx)
|
||||
|
||||
render.JSON(w, s, 200)
|
||||
render.JSON(w, http.StatusOK, s)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package space
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -38,7 +37,7 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to count child spaces.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to list child spaces.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -54,6 +53,8 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
* Only list spaces that are either public or can be accessed by the user
|
||||
*
|
||||
* TODO: optimize by making a single auth check for all spaces at once.
|
||||
* TODO: maybe ommit permission check for performance.
|
||||
* TODO: count is off in case not all repos are accessible.
|
||||
*/
|
||||
result := make([]*types.Space, 0, len(allSpaces))
|
||||
for _, cs := range allSpaces {
|
||||
|
@ -70,6 +71,6 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
render.Pagination(r, w, params.Page, params.Size, int(count))
|
||||
render.JSON(w, result, 200)
|
||||
render.JSON(w, http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package space
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -37,11 +36,11 @@ func HandleListPaths(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFu
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get list of space paths.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: do we need pagination? we should block that many paths in the first place.
|
||||
render.JSON(w, paths, 200)
|
||||
render.JSON(w, http.StatusOK, paths)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package space
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -38,7 +37,7 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to count child repos.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to list child repos.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -54,6 +53,8 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
* Only list repos that are either public or can be accessed by the user
|
||||
*
|
||||
* TODO: optimize by making a single auth check for all repos at once.
|
||||
* TODO: maybe ommit permission check for performance.
|
||||
* TODO: count is off in case not all repos are accessible.
|
||||
*/
|
||||
result := make([]*types.Repository, 0, len(allRepos))
|
||||
for _, rep := range allRepos {
|
||||
|
@ -71,6 +72,6 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
}
|
||||
|
||||
render.Pagination(r, w, params.Page, params.Size, int(count))
|
||||
render.JSON(w, result, 200)
|
||||
render.JSON(w, http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,15 +6,12 @@ package space
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -62,10 +59,10 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
|
||||
// ensure we don't end up in any missconfiguration, and block no-ops
|
||||
if err = check.Name(*in.Name); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
} else if *in.ParentId == space.ParentId && *in.Name == space.Name {
|
||||
render.BadRequest(w, errs.NoChangeInRequestedMove)
|
||||
render.BadRequestError(w, render.ErrNoChange)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -73,14 +70,11 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
// Ensure we can create spaces within the target space (using parent space as scope, similar to create)
|
||||
if *in.ParentId > 0 && *in.ParentId != space.ParentId {
|
||||
newParent, err := spaces.Find(ctx, *in.ParentId)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Parent space not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Msgf("Failed to get target space with id %d for the move.", *in.ParentId)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -94,37 +88,24 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
/*
|
||||
* Validate path (Due to racing conditions we can't be 100% sure on the path here only best effort to avoid big transaction failure)
|
||||
* Validate path length (Due to racing conditions we can't be 100% sure on the path here only best effort to avoid big transaction failure)
|
||||
* Only needed if we actually change the parent (and can skip top level, as we already validate the name)
|
||||
*/
|
||||
path := paths.Concatinate(newParent.Path, *in.Name)
|
||||
if err = check.PathParams(path, true); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
if err = check.Path(path, true); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res, err := spaces.Move(ctx, usr.ID, space.ID, *in.ParentId, *in.Name, in.KeepAsAlias)
|
||||
if errors.Is(err, errs.Duplicate) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to move the space as a duplicate was detected.")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to move the space.")
|
||||
|
||||
render.BadRequestf(w, "Unable to move the space as the destination path is already taken.")
|
||||
return
|
||||
} else if errors.Is(err, errs.PathTooLong) {
|
||||
log.Warn().Err(err).
|
||||
Msg("Failed to move the space as a path was too long.")
|
||||
|
||||
render.BadRequestf(w, "Unable to move the space as the destination path of the space or one of its child resources was too long.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Failed to move the space.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, res, http.StatusOK)
|
||||
render.JSON(w, http.StatusOK, res)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
|
@ -59,19 +58,18 @@ func HandleUpdate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
|
||||
// ensure provided values are valid
|
||||
if err := check.Space(space); err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = spaces.Update(ctx, space)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Msg("Space update failed.")
|
||||
log.Error().Err(err).Msg("Space update failed.")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, space, http.StatusOK)
|
||||
render.JSON(w, http.StatusOK, space)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleFind() http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user, _ := request.UserFrom(ctx)
|
||||
render.JSON(w, user, 200)
|
||||
render.JSON(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,6 @@ func HandleCurrent() http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user, _ := request.UserFrom(ctx)
|
||||
platform.RenderResource(w, user, 200)
|
||||
platform.RenderResource(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package user
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
|
@ -25,13 +24,12 @@ func HandleToken(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
token, err := token.Generate(user, user.Salt)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Msg("failed to generate token")
|
||||
log.Err(err).Msg("failed to generate token")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, &types.Token{Value: token}, 200)
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
if err != nil {
|
||||
log.Err(err).Msg("Failed to hash password.")
|
||||
|
||||
render.InternalError(w, err)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
user.Password = string(hash)
|
||||
|
@ -60,10 +60,10 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
if err != nil {
|
||||
log.Err(err).Msg("Failed to update the user.")
|
||||
|
||||
render.InternalError(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, user, 200)
|
||||
render.JSON(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,9 +120,9 @@ func TestUpdate_HashError(t *testing.T) {
|
|||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(render.Error), &render.Error{Message: bcrypt.ErrHashTooShort.Error()}
|
||||
got := new(render.Error)
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
if diff := cmp.Diff(got.Message, render.ErrInternal.Message); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
@ -151,9 +151,9 @@ func TestUpdate_BadRequest(t *testing.T) {
|
|||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(render.Error), &render.Error{Message: "EOF"}
|
||||
got := new(render.Error)
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
if diff := cmp.Diff(got.Message, "Invalid request body: EOF."); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ func TestUpdate_ServerError(t *testing.T) {
|
|||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(render.Error), render.ErrNotFound
|
||||
got, want := new(render.Error), render.ErrInternal
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -46,7 +45,7 @@ func HandleCreate(users store.UserStore) http.HandlerFunc {
|
|||
Str("email", in.Username).
|
||||
Msg("Failed to hash password")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -64,7 +63,7 @@ func HandleCreate(users store.UserStore) http.HandlerFunc {
|
|||
Str("email", user.Email).
|
||||
Msg("invalid user input")
|
||||
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -74,10 +73,10 @@ func HandleCreate(users store.UserStore) http.HandlerFunc {
|
|||
Str("email", user.Email).
|
||||
Msg("failed to create user")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, user, 200)
|
||||
render.JSON(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
||||
|
@ -26,13 +23,10 @@ func HandleDelete(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
key := chi.URLParam(r, "user")
|
||||
user, err := users.FindKey(ctx, key)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "User not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get user using key '%s'.", key)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -43,7 +37,7 @@ func HandleDelete(users store.UserStore) http.HandlerFunc {
|
|||
Str("user_email", user.Email).
|
||||
Msg("failed to delete user")
|
||||
|
||||
render.InternalError(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
||||
}
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
||||
|
@ -26,16 +23,13 @@ func HandleFind(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
key := chi.URLParam(r, "user")
|
||||
user, err := users.FindKey(ctx, key)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "User doesn't exist.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get user using key '%s'.", key)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msgf("Failed to get user using key '%s'.", key)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, user, 200)
|
||||
render.JSON(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,12 +37,11 @@ func HandleList(users store.UserStore) http.HandlerFunc {
|
|||
log.Err(err).
|
||||
Msg("Failed to retrieve user list")
|
||||
|
||||
render.InternalError(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Pagination(r, w, params.Page, params.Size, int(count))
|
||||
render.JSON(w, list, 200)
|
||||
|
||||
render.JSON(w, http.StatusOK, list)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,11 @@ package users
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gotidy/ptr"
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
@ -36,13 +33,10 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
key := chi.URLParam(r, "user")
|
||||
user, err := users.FindKey(ctx, key)
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "User not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get user using key '%s'.", key)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msgf("Failed to get user using key '%s'.", key)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,7 +54,7 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
Str("user_email", user.Email).
|
||||
Msg("Failed to hash password")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
user.Password = string(hash)
|
||||
|
@ -87,7 +81,7 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
Str("user_email", user.Email).
|
||||
Msg("Failed to hash password")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
user.Password = string(hash)
|
||||
|
@ -99,7 +93,7 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
Str("user_email", user.Email).
|
||||
Msg("invalid user input")
|
||||
|
||||
render.BadRequest(w, err)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,10 +106,10 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
Str("user_email", user.Email).
|
||||
Msg("Failed to update the usser")
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, user, 200)
|
||||
render.JSON(w, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package accesslog
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
* A simple middleware that logs completed requests using the default hlog access handler.
|
||||
*/
|
||||
func HlogHandler() func(http.Handler) http.Handler {
|
||||
return hlog.AccessHandler(
|
||||
func(r *http.Request, status, size int, duration time.Duration) {
|
||||
hlog.FromRequest(r).Info().
|
||||
Str("method", r.Method).
|
||||
Stringer("url", r.URL).
|
||||
Int("status_code", status).
|
||||
Int("response_size_bytes", size).
|
||||
Dur("elapsed_ms", duration).
|
||||
Msg("request completed.")
|
||||
},
|
||||
)
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
package authn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
"github.com/harness/gitness/internal/auth/authn"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -22,21 +23,29 @@ import (
|
|||
func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := authenticator.Authenticate(r)
|
||||
if err != nil {
|
||||
render.Unauthorized(w, err)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
log := hlog.FromRequest(r)
|
||||
|
||||
// if there was no auth info - continue as is
|
||||
if user == nil {
|
||||
user, err := authenticator.Authenticate(r)
|
||||
|
||||
if errors.Is(err, authn.ErrNoAuthData) {
|
||||
// if there was no auth data in the request - continue as is
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
} else if err != nil {
|
||||
// for any other error we fail
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
} else if user == nil {
|
||||
// when err == nil user should never be nil!
|
||||
log.Error().Msg("User is nil eventhough the authenticator didn't return any error!")
|
||||
|
||||
render.InternalError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the logging context and inject user in context
|
||||
ctx := r.Context()
|
||||
log.Ctx(ctx).UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Int64("user_id", user.ID).Bool("user_admin", user.Admin)
|
||||
})
|
||||
|
||||
|
|
|
@ -5,18 +5,15 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -27,13 +24,15 @@ import (
|
|||
func Required(repos store.RepoStore) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
log := hlog.FromRequest(r)
|
||||
|
||||
ref, err := request.GetRepoRef(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
var repo *types.Repository
|
||||
|
||||
// check if ref is repoId - ASSUMPTION: digit only is no valid repo name
|
||||
|
@ -44,18 +43,15 @@ func Required(repos store.RepoStore) func(http.Handler) http.Handler {
|
|||
repo, err = repos.FindByPath(ctx, ref)
|
||||
}
|
||||
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Repository doesn't exist.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get repo using ref '%s'.", ref)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msgf("Failed to get repo using ref '%s'.", ref)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the logging context and inject repo in context
|
||||
log.Ctx(ctx).UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Int64("repo_id", repo.ID).Str("repo_path", repo.Path)
|
||||
})
|
||||
|
||||
|
|
|
@ -5,18 +5,15 @@
|
|||
package space
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/harness/gitness/internal/api/comms"
|
||||
"github.com/harness/gitness/internal/api/render"
|
||||
"github.com/harness/gitness/internal/api/request"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -27,13 +24,15 @@ import (
|
|||
func Required(spaces store.SpaceStore) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
log := hlog.FromRequest(r)
|
||||
|
||||
ref, err := request.GetSpaceRef(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
var space *types.Space
|
||||
|
||||
// check if ref is spaceId - ASSUMPTION: digit only is no valid space name
|
||||
|
@ -44,18 +43,15 @@ func Required(spaces store.SpaceStore) func(http.Handler) http.Handler {
|
|||
space, err = spaces.FindByPath(ctx, ref)
|
||||
}
|
||||
|
||||
if errors.Is(err, errs.ResourceNotFound) {
|
||||
render.NotFoundf(w, "Space not found.")
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space using ref '%s'.", ref)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msgf("Failed to get space using ref '%s'.", ref)
|
||||
|
||||
render.InternalErrorf(w, comms.Internal)
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the logging context and inject repo in context
|
||||
log.Ctx(ctx).UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Int64("space_id", space.ID).Str("space_path", space.Path)
|
||||
})
|
||||
|
||||
|
|
|
@ -5,9 +5,15 @@
|
|||
package render
|
||||
|
||||
var (
|
||||
// ErrInternal is returned when an internal error occured.
|
||||
ErrInternal = New("Internal error occured")
|
||||
|
||||
// ErrInvalidToken is returned when the api request token is invalid.
|
||||
ErrInvalidToken = New("Invalid or missing token")
|
||||
|
||||
// ErrBadRequest is returned when there was an issue with the user input.
|
||||
ErrBadRequest = New("Bad Request")
|
||||
|
||||
// ErrUnauthorized is returned when the user is not authorized.
|
||||
ErrUnauthorized = New("Unauthorized")
|
||||
|
||||
|
@ -16,6 +22,24 @@ var (
|
|||
|
||||
// ErrNotFound is returned when a resource is not found.
|
||||
ErrNotFound = New("Not Found")
|
||||
|
||||
// ErrNoChange is returned when no change was found based on the request.
|
||||
ErrNoChange = New("No Change")
|
||||
|
||||
// ErrDuplicate is returned when a resource already exits.
|
||||
ErrDuplicate = New("Resource already exists")
|
||||
|
||||
// ErrPrimaryPathCantBeDeleted is returned when the user is trying to delete a primary path.
|
||||
ErrPrimaryPathCantBeDeleted = New("The primary path of an object can't be deleted")
|
||||
|
||||
// ErrPathTooLong is returned if user action would lead to a path that is too long.
|
||||
ErrPathTooLong = New("The resource path is too long")
|
||||
|
||||
// ErrCyclicHierarchy is returned if the user action would create a cyclic dependency between spaces
|
||||
ErrCyclicHierarchy = New(("Unable to perform the action as it would lead to a cyclic dependency."))
|
||||
|
||||
// ErrSpaceWithChildsCantBeDeleted is returned if the user is trying to delete a space that still has child resources
|
||||
ErrSpaceWithChildsCantBeDeleted = New("Space can't be deleted as it still contains child resources.")
|
||||
)
|
||||
|
||||
// Error represents a json-encoded API error.
|
||||
|
@ -28,6 +52,6 @@ func (e *Error) Error() string {
|
|||
}
|
||||
|
||||
// New returns a new error message.
|
||||
func New(text string) error {
|
||||
func New(text string) *Error {
|
||||
return &Error{Message: text}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ package render
|
|||
import "testing"
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
got, want := ErrNotFound.Error(), ErrNotFound.(*Error).Message
|
||||
got, want := ErrNotFound.Message, ErrNotFound.Message
|
||||
if got != want {
|
||||
t.Errorf("Want error string %q, got %q", got, want)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
// RenderResource is a helper function that renders a single
|
||||
// resource, wrapped in the harness payload envelope.
|
||||
func RenderResource(w http.ResponseWriter, v interface{}, code int) {
|
||||
func RenderResource(w http.ResponseWriter, code int, v interface{}) {
|
||||
payload := new(wrapper)
|
||||
payload.Status = "SUCCESS"
|
||||
payload.Data, _ = json.Marshal(v)
|
||||
|
@ -18,7 +18,7 @@ func RenderResource(w http.ResponseWriter, v interface{}, code int) {
|
|||
} else if code > 299 {
|
||||
payload.Status = "FAILURE"
|
||||
}
|
||||
render.JSON(w, payload, code)
|
||||
render.JSON(w, code, payload)
|
||||
}
|
||||
|
||||
// wrapper defines the payload wrapper.
|
||||
|
|
|
@ -6,10 +6,14 @@ package render
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/check"
|
||||
)
|
||||
|
||||
// indent the json-encoded API responses
|
||||
|
@ -21,77 +25,86 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
// ErrorCode writes the json-encoded error message to the response.
|
||||
func ErrorCode(w http.ResponseWriter, err error, status int) {
|
||||
JSON(w, &Error{Message: err.Error()}, status)
|
||||
/*
|
||||
* UserfiedErrorOrInternal renders the appropriate user facing message for the provided error.
|
||||
* If the error is unknown, an internal error is rendered.
|
||||
*/
|
||||
func UserfiedErrorOrInternal(w http.ResponseWriter, err error) {
|
||||
|
||||
if errors.Is(err, check.ErrAny) {
|
||||
ErrorObject(w, http.StatusBadRequest, &Error{err.Error()})
|
||||
} else if errors.Is(err, store.ErrResourceNotFound) {
|
||||
ErrorObject(w, http.StatusNotFound, ErrNotFound)
|
||||
} else if errors.Is(err, store.ErrDuplicate) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrDuplicate)
|
||||
} else if errors.Is(err, store.ErrPrimaryPathCantBeDeleted) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrPrimaryPathCantBeDeleted)
|
||||
} else if errors.Is(err, store.ErrPathTooLong) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrPathTooLong)
|
||||
} else if errors.Is(err, store.ErrNoChangeInRequestedMove) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrNoChange)
|
||||
} else if errors.Is(err, store.ErrIllegalMoveCyclicHierarchy) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrCyclicHierarchy)
|
||||
} else if errors.Is(err, store.ErrSpaceWithChildsCantBeDeleted) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrSpaceWithChildsCantBeDeleted)
|
||||
} else {
|
||||
// nothing found - render internal error
|
||||
fmt.Println(err)
|
||||
InternalError(w)
|
||||
}
|
||||
}
|
||||
|
||||
// InternalError writes the json-encoded error message to the response
|
||||
// with a 500 internal server error.
|
||||
func InternalError(w http.ResponseWriter, err error) {
|
||||
ErrorCode(w, err, 500)
|
||||
// NotFound writes the json-encoded message for a not found error.
|
||||
func NotFound(w http.ResponseWriter) {
|
||||
ErrorObject(w, http.StatusNotFound, ErrNotFound)
|
||||
}
|
||||
|
||||
// InternalErrorf writes the json-encoded error message to the response
|
||||
// with a 500 internal server error.
|
||||
func InternalErrorf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||
ErrorCode(w, fmt.Errorf(format, a...), 500)
|
||||
// Unauthorized writes the json-encoded message for an unauthorized error.
|
||||
func Unauthorized(w http.ResponseWriter) {
|
||||
ErrorObject(w, http.StatusUnauthorized, ErrUnauthorized)
|
||||
}
|
||||
|
||||
// NotFound writes the json-encoded error message to the response
|
||||
// with a 404 not found status code.
|
||||
func NotFound(w http.ResponseWriter, err error) {
|
||||
ErrorCode(w, err, 404)
|
||||
// Forbidden writes the json-encoded message for a forbidden error.
|
||||
func Forbidden(w http.ResponseWriter) {
|
||||
ErrorObject(w, http.StatusForbidden, ErrForbidden)
|
||||
}
|
||||
|
||||
// NotFoundf writes the json-encoded error message to the response
|
||||
// with a 404 not found status code.
|
||||
func NotFoundf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||
ErrorCode(w, fmt.Errorf(format, a...), 404)
|
||||
// BadRequest writes the json-encoded message for a bad request error.
|
||||
func BadRequest(w http.ResponseWriter) {
|
||||
ErrorObject(w, http.StatusBadRequest, ErrBadRequest)
|
||||
}
|
||||
|
||||
// Unauthorized writes the json-encoded error message to the response
|
||||
// with a 401 unauthorized status code.
|
||||
func Unauthorized(w http.ResponseWriter, err error) {
|
||||
ErrorCode(w, err, 401)
|
||||
// BadRequestError writes the json-encoded error with a bad request status code.
|
||||
func BadRequestError(w http.ResponseWriter, err *Error) {
|
||||
ErrorObject(w, http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
// Unauthorizedf writes the json-encoded error message to the response
|
||||
// with a 401 unauthorized status code.
|
||||
func Unauthorizedf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||
ErrorCode(w, fmt.Errorf(format, a...), 401)
|
||||
// BadRequest writes the json-encoded message with a bad request status code.
|
||||
func BadRequestf(w http.ResponseWriter, format string, args ...interface{}) {
|
||||
ErrorMessagef(w, http.StatusBadRequest, format, args...)
|
||||
}
|
||||
|
||||
// Forbidden writes the json-encoded error message to the response
|
||||
// with a 403 forbidden status code.
|
||||
func Forbidden(w http.ResponseWriter, err error) {
|
||||
ErrorCode(w, err, 403)
|
||||
// InternalError writes the json-encoded message for an internal error.
|
||||
func InternalError(w http.ResponseWriter) {
|
||||
ErrorObject(w, http.StatusInternalServerError, ErrInternal)
|
||||
}
|
||||
|
||||
// Forbiddenf writes the json-encoded error message to the response
|
||||
// with a 403 forbidden status code.
|
||||
func Forbiddenf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||
ErrorCode(w, fmt.Errorf(format, a...), 403)
|
||||
// ErrorMessagef writes the json-encoded, formated error message.
|
||||
func ErrorMessagef(w http.ResponseWriter, code int, format string, args ...interface{}) {
|
||||
JSON(w, code, &Error{Message: fmt.Sprintf(format, args...)})
|
||||
}
|
||||
|
||||
// BadRequest writes the json-encoded error message to the response
|
||||
// with a 400 bad request status code.
|
||||
func BadRequest(w http.ResponseWriter, err error) {
|
||||
ErrorCode(w, err, 400)
|
||||
// ErrorMessagef writes the json-encoded, formated error message.
|
||||
func ErrorObject(w http.ResponseWriter, code int, err *Error) {
|
||||
JSON(w, code, err)
|
||||
}
|
||||
|
||||
// BadRequestf writes the json-encoded error message to the response
|
||||
// with a 400 bad request status code.
|
||||
func BadRequestf(w http.ResponseWriter, format string, a ...interface{}) {
|
||||
ErrorCode(w, fmt.Errorf(format, a...), 400)
|
||||
}
|
||||
|
||||
// JSON writes the json-encoded error message to the response
|
||||
// with a 400 bad request status code.
|
||||
func JSON(w http.ResponseWriter, v interface{}, status int) {
|
||||
// JSON writes the json-encoded value to the response
|
||||
// with the provides status
|
||||
func JSON(w http.ResponseWriter, code int, v interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(status)
|
||||
w.WriteHeader(code)
|
||||
enc := json.NewEncoder(w)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
|
|
|
@ -11,27 +11,11 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestWriteError(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
InternalError(w, err)
|
||||
|
||||
if got, want := w.Code, 500; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteErrorf(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
InternalErrorf(w, "pc load letter")
|
||||
e := New("abc")
|
||||
ErrorObject(w, 500, e)
|
||||
|
||||
if got, want := w.Code, 500; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -39,7 +23,7 @@ func TestWriteErrorf(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, "pc load letter"; got != want {
|
||||
if got, want := errjson.Message, e.Message; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +31,7 @@ func TestWriteErrorf(t *testing.T) {
|
|||
func TestWriteErrorCode(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
ErrorCode(w, err, 418)
|
||||
ErrorMessagef(w, 418, "pc load letter %d", 1)
|
||||
|
||||
if got, want := w.Code, 418; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -56,7 +39,7 @@ func TestWriteErrorCode(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
if got, want := errjson.Message, "pc load letter 1"; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +47,7 @@ func TestWriteErrorCode(t *testing.T) {
|
|||
func TestWriteNotFound(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
NotFound(w, err)
|
||||
NotFound(w)
|
||||
|
||||
if got, want := w.Code, 404; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -73,23 +55,7 @@ func TestWriteNotFound(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteNotFoundf(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
NotFoundf(w, "pc load letter")
|
||||
|
||||
if got, want := w.Code, 404; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, "pc load letter"; got != want {
|
||||
if got, want := errjson.Message, ErrNotFound.Message; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -97,8 +63,7 @@ func TestWriteNotFoundf(t *testing.T) {
|
|||
func TestWriteUnauthorized(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
Unauthorized(w, err)
|
||||
Unauthorized(w)
|
||||
|
||||
if got, want := w.Code, 401; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -106,7 +71,7 @@ func TestWriteUnauthorized(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
if got, want := errjson.Message, ErrUnauthorized.Message; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +79,7 @@ func TestWriteUnauthorized(t *testing.T) {
|
|||
func TestWriteForbidden(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
Forbidden(w, err)
|
||||
Forbidden(w)
|
||||
|
||||
if got, want := w.Code, 403; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -123,7 +87,7 @@ func TestWriteForbidden(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
if got, want := errjson.Message, ErrForbidden.Message; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -131,8 +95,7 @@ func TestWriteForbidden(t *testing.T) {
|
|||
func TestWriteBadRequest(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := New("pc load letter")
|
||||
BadRequest(w, err)
|
||||
BadRequest(w)
|
||||
|
||||
if got, want := w.Code, 400; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
|
@ -140,23 +103,7 @@ func TestWriteBadRequest(t *testing.T) {
|
|||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, err.Error(); got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteBadRequestf(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
BadRequestf(w, "pc load letter")
|
||||
|
||||
if got, want := w.Code, 400; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
errjson := &Error{}
|
||||
json.NewDecoder(w.Body).Decode(errjson)
|
||||
if got, want := errjson.Message, "pc load letter"; got != want {
|
||||
if got, want := errjson.Message, ErrBadRequest.Message; got != want {
|
||||
t.Errorf("Want error message %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +112,7 @@ func TestWriteJSON(t *testing.T) {
|
|||
// without indent
|
||||
{
|
||||
w := httptest.NewRecorder()
|
||||
JSON(w, map[string]string{"hello": "world"}, http.StatusTeapot)
|
||||
JSON(w, http.StatusTeapot, map[string]string{"hello": "world"})
|
||||
if got, want := w.Body.String(), "{\"hello\":\"world\"}\n"; got != want {
|
||||
t.Errorf("Want JSON body %q, got %q", want, got)
|
||||
}
|
||||
|
@ -183,7 +130,7 @@ func TestWriteJSON(t *testing.T) {
|
|||
indent = false
|
||||
}()
|
||||
w := httptest.NewRecorder()
|
||||
JSON(w, map[string]string{"hello": "world"}, http.StatusTeapot)
|
||||
JSON(w, http.StatusTeapot, map[string]string{"hello": "world"})
|
||||
if got, want := w.Body.String(), "{\n \"hello\": \"world\"\n}\n"; got != want {
|
||||
t.Errorf("Want JSON body %q, got %q", want, got)
|
||||
}
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
)
|
||||
|
||||
const (
|
||||
RepoRefParamName = "rref"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRepoReferenceNotFound = errors.New("No repository reference found in request.")
|
||||
)
|
||||
|
||||
func GetRepoRef(r *http.Request) (string, error) {
|
||||
rawRef := chi.URLParam(r, RepoRefParamName)
|
||||
if rawRef == "" {
|
||||
return "", errs.RepoReferenceNotFoundInRequest
|
||||
return "", ErrRepoReferenceNotFound
|
||||
}
|
||||
|
||||
// paths are unescaped and lower
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
)
|
||||
|
||||
const (
|
||||
SpaceRefParamName = "sref"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSpaceReferenceNotFound = errors.New("No space reference found in request.")
|
||||
)
|
||||
|
||||
func GetSpaceRef(r *http.Request) (string, error) {
|
||||
rawRef := chi.URLParam(r, SpaceRefParamName)
|
||||
if rawRef == "" {
|
||||
return "", errs.SpaceReferenceNotFoundInRequest
|
||||
return "", ErrSpaceReferenceNotFound
|
||||
}
|
||||
|
||||
// paths are unescaped and lower
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
package authn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// An error that is returned if the authorizer doesn't find any data in the request that can be used for auth.
|
||||
ErrNoAuthData = errors.New("The request doesn't contain any auth data that can be used by the Authorizer.")
|
||||
)
|
||||
|
||||
/*
|
||||
* An abstraction of an entity thats responsible for authenticating users
|
||||
* that are making calls via HTTP.
|
||||
|
@ -18,9 +24,9 @@ type Authenticator interface {
|
|||
/*
|
||||
* Tries to authenticate a user if credentials are available.
|
||||
* Returns:
|
||||
* (user, nil) - request contained auth data and user was verified
|
||||
* (nil, err) - request contained auth data but verification failed
|
||||
* (nil, nil) - request didn't contain any auth data
|
||||
* (user, nil) - request contains auth data and user was verified
|
||||
* (nil, ErrNoAuthData) - request doesn't contain any auth data
|
||||
* (nil, err) - request contains auth data but verification failed
|
||||
*/
|
||||
Authenticate(r *http.Request) (*types.User, error)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func (a *TokenAuthenticator) Authenticate(r *http.Request) (*types.User, error)
|
|||
str := extractToken(r)
|
||||
|
||||
if len(str) == 0 {
|
||||
return nil, nil
|
||||
return nil, ErrNoAuthData
|
||||
}
|
||||
|
||||
var user *types.User
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
package authz
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
var (
|
||||
// An error that is thrown if no permission checks are provided
|
||||
ErrNoPermissionCheckProvided = errors.New("No permission checks provided")
|
||||
)
|
||||
|
||||
/*
|
||||
* An abstraction of an entity responsible for authorizing access to resources.
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
@ -48,7 +47,7 @@ func (a *Authorizer) Check(principalType enum.PrincipalType, principalId string,
|
|||
|
||||
func (a *Authorizer) CheckAll(principalType enum.PrincipalType, principalId string, permissionChecks ...*types.PermissionCheck) (bool, error) {
|
||||
if len(permissionChecks) == 0 {
|
||||
return false, errs.NoPermissionCheckProvided
|
||||
return false, authz.ErrNoPermissionCheckProvided
|
||||
}
|
||||
|
||||
requestDto, err := createAclRequest(principalType, principalId, permissionChecks)
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package errs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Static errors
|
||||
var (
|
||||
// Indicates that a requested resource wasn't found.
|
||||
ResourceNotFound error = &dynamicError{0, "Resource not found", nil}
|
||||
Duplicate error = &dynamicError{1, "Resource is a duplicate", nil}
|
||||
PathTooLong error = &dynamicError{2, "The path is too long", nil}
|
||||
)
|
||||
|
||||
// Wrappers
|
||||
func WrapInResourceNotFound(inner error) error {
|
||||
return cloneWithNewInner(ResourceNotFound.(*dynamicError), inner)
|
||||
}
|
||||
func WrapInDuplicate(inner error) error {
|
||||
return cloneWithNewInner(Duplicate.(*dynamicError), inner)
|
||||
}
|
||||
func WrapInPathTooLongf(format string, args ...interface{}) error {
|
||||
return cloneWithNewMsg(PathTooLong.(*dynamicError), fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Error type (on purpose not using explicit definitions and iota, to make overhead as small as possible)
|
||||
type dynamicErrorType int
|
||||
|
||||
/*
|
||||
* This is an abstraction of an error that can be both a standalone error or a wrapping error.
|
||||
* The idea is to allow errors.Is(err, errs.MyError) for wrapping errors while keeping code to a minimum
|
||||
*/
|
||||
type dynamicError struct {
|
||||
errorType dynamicErrorType
|
||||
msg string
|
||||
inner error
|
||||
}
|
||||
|
||||
func (e *dynamicError) Error() string {
|
||||
if e.inner == nil {
|
||||
return e.msg
|
||||
} else {
|
||||
return fmt.Sprintf("%s: %s", e.msg, e.inner)
|
||||
}
|
||||
}
|
||||
func (e *dynamicError) Unwrap() error {
|
||||
return e.inner
|
||||
}
|
||||
|
||||
func (e *dynamicError) Is(target error) bool {
|
||||
te, ok := target.(*dynamicError)
|
||||
return ok && te.errorType == e.errorType
|
||||
}
|
||||
|
||||
func cloneWithNewMsg(d *dynamicError, msg string) *dynamicError {
|
||||
return &dynamicError{d.errorType, msg, nil}
|
||||
}
|
||||
|
||||
func cloneWithNewInner(d *dynamicError, inner error) *dynamicError {
|
||||
return &dynamicError{d.errorType, d.msg, inner}
|
||||
}
|
||||
|
||||
func cloneWithNewMsgAndInner(d *dynamicError, msg string, inner error) *dynamicError {
|
||||
return &dynamicError{d.errorType, msg, inner}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package errs
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
NotAuthenticated = errors.New("Not authenticated.")
|
||||
NotAuthorized = errors.New("Not authorized.")
|
||||
RepositoryRequired = errors.New("The operation requires a repository.")
|
||||
PathEmpty = errors.New("Path is empty.")
|
||||
PrimaryPathAlreadyExists = errors.New("Primary path already exists for resource.")
|
||||
AliasPathRequired = errors.New("Path has to be an alias.")
|
||||
PrimaryPathRequired = errors.New("Path has to be primary.")
|
||||
PrimaryPathCantBeDeleted = errors.New("Primary path can't be deleted.")
|
||||
NoChangeInRequestedMove = errors.New(("The requested move doesn't change anything."))
|
||||
IllegalMoveCyclicHierarchy = errors.New(("The requested move is not permitted as it would cause a cyclic depdency."))
|
||||
SpaceWithChildsCantBeDeleted = errors.New("The space can't be deleted as it still contains spaces or repos.")
|
||||
RepoReferenceNotFoundInRequest = errors.New("No repository reference found in request.")
|
||||
SpaceReferenceNotFoundInRequest = errors.New("No space reference found in request.")
|
||||
NoPermissionCheckProvided = errors.New("No permission checks provided")
|
||||
)
|
|
@ -5,19 +5,23 @@
|
|||
package paths
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPathEmpty = errors.New("Path is empty.")
|
||||
)
|
||||
|
||||
/*
|
||||
* Splits a path into its parent path and the leaf name.
|
||||
* e.g. /space1/space2/space3 -> (/space1/space2, space3, nil)
|
||||
*/
|
||||
func Disect(path string) (string, string, error) {
|
||||
if path == "" {
|
||||
return "", "", errs.PathEmpty
|
||||
return "", "", ErrPathEmpty
|
||||
}
|
||||
|
||||
i := strings.LastIndex(path, types.PathSeparator)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
handler_space "github.com/harness/gitness/internal/api/handler/space"
|
||||
"github.com/harness/gitness/internal/api/handler/system"
|
||||
"github.com/harness/gitness/internal/api/handler/user"
|
||||
"github.com/harness/gitness/internal/api/middleware/accesslog"
|
||||
middleware_authn "github.com/harness/gitness/internal/api/middleware/authn"
|
||||
"github.com/harness/gitness/internal/api/middleware/encode"
|
||||
"github.com/harness/gitness/internal/api/middleware/repo"
|
||||
|
@ -55,6 +56,7 @@ func newApiHandler(
|
|||
r.Use(hlog.URLHandler("path"))
|
||||
r.Use(hlog.MethodHandler("method"))
|
||||
r.Use(hlog.RequestIDHandler("request", "Request-Id"))
|
||||
r.Use(accesslog.HlogHandler())
|
||||
|
||||
// configure cors middleware
|
||||
cors := cors.New(
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/middleware/accesslog"
|
||||
middleware_authn "github.com/harness/gitness/internal/api/middleware/authn"
|
||||
"github.com/harness/gitness/internal/api/middleware/encode"
|
||||
"github.com/harness/gitness/internal/api/middleware/repo"
|
||||
|
@ -48,6 +49,7 @@ func newGitHandler(
|
|||
r.Use(hlog.URLHandler("path"))
|
||||
r.Use(hlog.MethodHandler("method"))
|
||||
r.Use(hlog.RequestIDHandler("request", "Request-Id"))
|
||||
r.Use(accesslog.HlogHandler())
|
||||
|
||||
// for now always attempt auth - enforced per operation
|
||||
r.Use(middleware_authn.Attempt(authenticator))
|
||||
|
@ -80,7 +82,6 @@ func newGitHandler(
|
|||
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", stubGitHandler)
|
||||
r.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", stubGitHandler)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -90,7 +91,7 @@ func newGitHandler(
|
|||
func stubGitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rep, _ := request.RepoFrom(r.Context())
|
||||
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
w.Write([]byte(fmt.Sprintf(
|
||||
"Oooops, seems you hit a major construction site ... \n"+
|
||||
" Repo: '%s' (%s)\n"+
|
||||
|
|
|
@ -2,7 +2,6 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
repo_id SERIAL PRIMARY KEY
|
||||
,repo_name TEXT
|
||||
,repo_spaceId INTEGER
|
||||
,repo_path TEXT
|
||||
,repo_displayName TEXT
|
||||
,repo_description TEXT
|
||||
,repo_isPublic BOOLEAN
|
||||
|
@ -14,5 +13,4 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
,repo_numPulls INTEGER
|
||||
,repo_numClosedPulls INTEGER
|
||||
,repo_numOpenPulls INTEGER
|
||||
,UNIQUE(repo_path)
|
||||
);
|
||||
|
|
|
@ -2,7 +2,6 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
repo_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,repo_name TEXT COLLATE NOCASE
|
||||
,repo_spaceId INTEGER
|
||||
,repo_path TEXT COLLATE NOCASE
|
||||
,repo_displayName TEXT
|
||||
,repo_description TEXT
|
||||
,repo_isPublic BOOLEAN
|
||||
|
@ -14,5 +13,4 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
,repo_numPulls INTEGER
|
||||
,repo_numClosedPulls INTEGER
|
||||
,repo_numOpenPulls INTEGER
|
||||
,UNIQUE(repo_path COLLATE NOCASE)
|
||||
);
|
||||
|
|
|
@ -8,39 +8,35 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Creates a new path
|
||||
func CreatePath(ctx context.Context, db *sqlx.DB, path *types.Path) error {
|
||||
// Creates a new alias path (Don't call this for new path creation!)
|
||||
func CreateAliasPath(ctx context.Context, db *sqlx.DB, path *types.Path) error {
|
||||
if !path.IsAlias {
|
||||
return store.ErrAliasPathRequired
|
||||
}
|
||||
|
||||
// ensure path length is okay
|
||||
if check.PathTooLong(path.Value, path.TargetType == enum.PathTargetTypeSpace) {
|
||||
return errs.WrapInPathTooLongf("Path '%s' is too long.", path.Value)
|
||||
}
|
||||
|
||||
// In case it's not an alias, ensure there are no duplicates
|
||||
if !path.IsAlias {
|
||||
if cnt, err := CountPaths(ctx, db, path.TargetType, path.TargetId); err != nil {
|
||||
return err
|
||||
} else if cnt > 0 {
|
||||
return errs.PrimaryPathAlreadyExists
|
||||
}
|
||||
log.Warn().Msgf("Path '%s' is too long.", path.Value)
|
||||
return store.ErrPathTooLong
|
||||
}
|
||||
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind path object")
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, arg...).Scan(&path.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "Insert query failed")
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -51,7 +47,8 @@ func CreatePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pat
|
|||
|
||||
// ensure path length is okay
|
||||
if check.PathTooLong(path.Value, path.TargetType == enum.PathTargetTypeSpace) {
|
||||
return errs.WrapInPathTooLongf("Path '%s' is too long.", path.Value)
|
||||
log.Warn().Msgf("Path '%s' is too long.", path.Value)
|
||||
return store.ErrPathTooLong
|
||||
}
|
||||
|
||||
// In case it's not an alias, ensure there are no duplicates
|
||||
|
@ -59,17 +56,17 @@ func CreatePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pat
|
|||
if cnt, err := CountPathsTx(ctx, tx, path.TargetType, path.TargetId); err != nil {
|
||||
return err
|
||||
} else if cnt > 0 {
|
||||
return errs.PrimaryPathAlreadyExists
|
||||
return store.ErrPrimaryPathAlreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind path object")
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRowContext(ctx, query, arg...).Scan(&path.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "Insert query failed")
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -79,7 +76,7 @@ func CountPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) (
|
|||
var count int64
|
||||
err := tx.QueryRowContext(ctx, pathCountPrimaryForPrefix, paths.Concatinate(prefix, "%")).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Count query failed")
|
||||
return 0, processSqlErrorf(err, "Count query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -88,7 +85,7 @@ func ListPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) ([
|
|||
childs := []*types.Path{}
|
||||
|
||||
if err := tx.SelectContext(ctx, &childs, pathSelectPrimaryForPrefix, paths.Concatinate(prefix, "%")); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
|
||||
return childs, nil
|
||||
|
@ -98,19 +95,20 @@ func ListPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) ([
|
|||
func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Path, keepAsAlias bool) error {
|
||||
|
||||
if path.IsAlias {
|
||||
return errs.PrimaryPathRequired
|
||||
return store.ErrPrimaryPathRequired
|
||||
}
|
||||
|
||||
// ensure new path length is okay
|
||||
if check.PathTooLong(path.Value, path.TargetType == enum.PathTargetTypeSpace) {
|
||||
return errs.WrapInPathTooLongf("Path '%s' is too long.", path.Value)
|
||||
log.Warn().Msgf("Path '%s' is too long.", path.Value)
|
||||
return store.ErrPathTooLong
|
||||
}
|
||||
|
||||
// existing is always non-alias (as query filters for IsAlias=0)
|
||||
existing := new(types.Path)
|
||||
err := tx.GetContext(ctx, existing, pathSelectPrimaryForTarget, string(path.TargetType), fmt.Sprint(path.TargetId))
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to get the existing primary path")
|
||||
return processSqlErrorf(err, "Failed to get the existing primary path")
|
||||
}
|
||||
|
||||
// Only look for childs if the type can have childs
|
||||
|
@ -134,17 +132,18 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
|
||||
// ensure new child path length is okay
|
||||
if check.PathTooLong(updatedChild.Value, path.TargetType == enum.PathTargetTypeSpace) {
|
||||
return errs.WrapInPathTooLongf("Path '%s' is too long.", updatedChild.Value)
|
||||
log.Warn().Msgf("Path '%s' is too long.", path.Value)
|
||||
return store.ErrPathTooLong
|
||||
}
|
||||
|
||||
query, arg, err := db.BindNamed(pathInsert, updatedChild)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind path object")
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to create new primary child path '%s'", updatedChild.Value)
|
||||
return processSqlErrorf(err, "Failed to create new primary child path '%s'", updatedChild.Value)
|
||||
}
|
||||
|
||||
// make current child an alias or delete it
|
||||
|
@ -154,7 +153,7 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
_, err = tx.ExecContext(ctx, pathDeleteId, child.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to mark existing child path '%s' as alias", updatedChild.Value)
|
||||
return processSqlErrorf(err, "Failed to mark existing child path '%s' as alias", updatedChild.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,12 +161,12 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
// insert the new Path
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind path object")
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to create new primary path '%s'", path.Value)
|
||||
return processSqlErrorf(err, "Failed to create new primary path '%s'", path.Value)
|
||||
}
|
||||
|
||||
// make existing an alias
|
||||
|
@ -177,7 +176,7 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
_, err = tx.ExecContext(ctx, pathDeleteId, existing.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to mark existing path '%s' as alias", existing.Value)
|
||||
return processSqlErrorf(err, "Failed to mark existing path '%s' as alias", existing.Value)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -188,7 +187,7 @@ func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType
|
|||
dst := new(types.Path)
|
||||
err := tx.GetContext(ctx, dst, pathSelectPrimaryForTarget, string(targetType), fmt.Sprint(targetId))
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -198,7 +197,7 @@ func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType
|
|||
func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error {
|
||||
tx, err := db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
|
@ -206,18 +205,18 @@ func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error {
|
|||
dst := new(types.Path)
|
||||
err = tx.GetContext(ctx, dst, pathSelectId, id)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to find path with id %d", id)
|
||||
return processSqlErrorf(err, "Failed to find path with id %d", id)
|
||||
} else if dst.IsAlias == false {
|
||||
return errs.PrimaryPathCantBeDeleted
|
||||
return store.ErrPrimaryPathCantBeDeleted
|
||||
}
|
||||
|
||||
// delete the path
|
||||
if _, err = tx.ExecContext(ctx, pathDeleteId, id); err != nil {
|
||||
return wrapSqlErrorf(err, "Delete query failed", id)
|
||||
return processSqlErrorf(err, "Delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -227,7 +226,7 @@ func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error {
|
|||
func DeleteAllPaths(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetId int64) error {
|
||||
// delete all entries for the target
|
||||
if _, err := tx.ExecContext(ctx, pathDeleteTarget, string(targetType), fmt.Sprint(targetId)); err != nil {
|
||||
return wrapSqlErrorf(err, "Query for deleting all pahts failed")
|
||||
return processSqlErrorf(err, "Query for deleting all pahts failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -241,7 +240,7 @@ func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType,
|
|||
if opts.Sort == enum.PathAttrNone {
|
||||
err := db.SelectContext(ctx, &dst, pathSelect, string(targetType), fmt.Sprint(targetId), limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Default select query failed")
|
||||
return nil, processSqlErrorf(err, "Default select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -272,7 +271,7 @@ func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType,
|
|||
}
|
||||
|
||||
if err = db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Customer select query failed")
|
||||
return nil, processSqlErrorf(err, "Customer select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -283,7 +282,7 @@ func CountPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType
|
|||
var count int64
|
||||
err := db.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetId)).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Query failed")
|
||||
return 0, processSqlErrorf(err, "Query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -293,7 +292,7 @@ func CountPathsTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetTy
|
|||
var count int64
|
||||
err := tx.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetId)).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Query failed")
|
||||
return 0, processSqlErrorf(err, "Query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -35,7 +34,7 @@ type RepoStore struct {
|
|||
func (s *RepoStore) Find(ctx context.Context, id int64) (*types.Repository, error) {
|
||||
dst := new(types.Repository)
|
||||
if err := s.db.GetContext(ctx, dst, repoSelectById, id); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -44,7 +43,7 @@ func (s *RepoStore) Find(ctx context.Context, id int64) (*types.Repository, erro
|
|||
func (s *RepoStore) FindByPath(ctx context.Context, path string) (*types.Repository, error) {
|
||||
dst := new(types.Repository)
|
||||
if err := s.db.GetContext(ctx, dst, repoSelectByPath, path); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -53,18 +52,18 @@ func (s *RepoStore) FindByPath(ctx context.Context, path string) (*types.Reposit
|
|||
func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// insert repo first so we get id
|
||||
query, arg, err := s.db.BindNamed(repoInsert, repo)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind repo object")
|
||||
return processSqlErrorf(err, "Failed to bind repo object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRow(query, arg...).Scan(&repo.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "Insert query failed")
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
// Get parent path (repo always has a parent)
|
||||
|
@ -93,7 +92,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
// update path in repo object
|
||||
|
@ -106,7 +105,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpaceId int64, newName string, keepAsAlias bool) (*types.Repository, error) {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return nil, processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
|
@ -124,7 +123,7 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
newPath := paths.Concatinate(spacePath.Value, newName)
|
||||
|
||||
if newPath == currentPath.Value {
|
||||
return nil, errs.NoChangeInRequestedMove
|
||||
return nil, store.ErrNoChangeInRequestedMove
|
||||
}
|
||||
|
||||
p := &types.Path{
|
||||
|
@ -144,18 +143,18 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
|
||||
// Rename the repo itself
|
||||
if _, err := tx.ExecContext(ctx, repoUpdateNameAndSpaceId, newName, newSpaceId, repoId); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Query for renaming and updating the space id failed")
|
||||
return nil, processSqlErrorf(err, "Query for renaming and updating the space id failed")
|
||||
}
|
||||
|
||||
// TODO: return repo as part of rename db operation?
|
||||
dst := new(types.Repository)
|
||||
if err = tx.GetContext(ctx, dst, repoSelectById, repoId); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query to get the repo's latest state failed")
|
||||
return nil, processSqlErrorf(err, "Select query to get the repo's latest state failed")
|
||||
}
|
||||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return nil, processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -165,11 +164,11 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
func (s *RepoStore) Update(ctx context.Context, repo *types.Repository) error {
|
||||
query, arg, err := s.db.BindNamed(repoUpdate, repo)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind repo object")
|
||||
return processSqlErrorf(err, "Failed to bind repo object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
wrapSqlErrorf(err, "Update query failed")
|
||||
processSqlErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -179,7 +178,7 @@ func (s *RepoStore) Update(ctx context.Context, repo *types.Repository) error {
|
|||
func (s *RepoStore) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
|
@ -191,11 +190,11 @@ func (s *RepoStore) Delete(ctx context.Context, id int64) error {
|
|||
|
||||
// delete the repo
|
||||
if _, err := tx.ExecContext(ctx, repoDelete, id); err != nil {
|
||||
return wrapSqlErrorf(err, "The delete query failed")
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -206,7 +205,7 @@ func (s *RepoStore) Count(ctx context.Context, spaceId int64) (int64, error) {
|
|||
var count int64
|
||||
err := s.db.QueryRow(repoCount, spaceId).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -221,7 +220,7 @@ func (s *RepoStore) List(ctx context.Context, spaceId int64, opts *types.RepoFil
|
|||
if opts.Sort == enum.RepoAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, repoSelect, spaceId, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -259,7 +258,7 @@ func (s *RepoStore) List(ctx context.Context, spaceId int64, opts *types.RepoFil
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -284,7 +283,7 @@ func (s *RepoStore) CreatePath(ctx context.Context, repoId int64, params *types.
|
|||
Updated: params.Updated,
|
||||
}
|
||||
|
||||
return p, CreatePath(ctx, s.db, p)
|
||||
return p, CreateAliasPath(ctx, s.db, p)
|
||||
}
|
||||
|
||||
// Delete an alias of a repo
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/paths"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -36,7 +35,7 @@ type SpaceStore struct {
|
|||
func (s *SpaceStore) Find(ctx context.Context, id int64) (*types.Space, error) {
|
||||
dst := new(types.Space)
|
||||
if err := s.db.GetContext(ctx, dst, spaceSelectById, id); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -45,7 +44,7 @@ func (s *SpaceStore) Find(ctx context.Context, id int64) (*types.Space, error) {
|
|||
func (s *SpaceStore) FindByPath(ctx context.Context, path string) (*types.Space, error) {
|
||||
dst := new(types.Space)
|
||||
if err := s.db.GetContext(ctx, dst, spaceSelectByPath, path); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query failed")
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -54,18 +53,18 @@ func (s *SpaceStore) FindByPath(ctx context.Context, path string) (*types.Space,
|
|||
func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// insert space first so we get id
|
||||
query, arg, err := s.db.BindNamed(spaceInsert, space)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind space object")
|
||||
return processSqlErrorf(err, "Failed to bind space object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRow(query, arg...).Scan(&space.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "Insert query failed")
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
// Get path (get parent if needed)
|
||||
|
@ -97,7 +96,7 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
// update path in space object
|
||||
|
@ -110,7 +109,7 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newParentId int64, newName string, keepAsAlias bool) (*types.Space, error) {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return nil, processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
|
@ -137,9 +136,9 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
* To avoid cycles in the primary graph, we have to ensure that the old path isn't a prefix of the new path.
|
||||
*/
|
||||
if newPath == currentPath.Value {
|
||||
return nil, errs.NoChangeInRequestedMove
|
||||
return nil, store.ErrNoChangeInRequestedMove
|
||||
} else if strings.HasPrefix(newPath, currentPath.Value) {
|
||||
return nil, errs.IllegalMoveCyclicHierarchy
|
||||
return nil, store.ErrIllegalMoveCyclicHierarchy
|
||||
}
|
||||
|
||||
p := &types.Path{
|
||||
|
@ -159,18 +158,18 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
|
||||
// Update the space itself
|
||||
if _, err := tx.ExecContext(ctx, spaceUpdateNameAndParentId, newName, newParentId, spaceId); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Query for renaming and updating the parent id failed")
|
||||
return nil, processSqlErrorf(err, "Query for renaming and updating the parent id failed")
|
||||
}
|
||||
|
||||
// TODO: return space as part of rename operation
|
||||
dst := new(types.Space)
|
||||
if err = tx.GetContext(ctx, dst, spaceSelectById, spaceId); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select query to get the space's latest state failed")
|
||||
return nil, processSqlErrorf(err, "Select query to get the space's latest state failed")
|
||||
}
|
||||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return nil, processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -180,11 +179,11 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
func (s *SpaceStore) Update(ctx context.Context, space *types.Space) error {
|
||||
query, arg, err := s.db.BindNamed(spaceUpdate, space)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind space object")
|
||||
return processSqlErrorf(err, "Failed to bind space object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
wrapSqlErrorf(err, "Update query failed")
|
||||
processSqlErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -194,7 +193,7 @@ func (s *SpaceStore) Update(ctx context.Context, space *types.Space) error {
|
|||
func (s *SpaceStore) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
|
@ -210,7 +209,7 @@ func (s *SpaceStore) Delete(ctx context.Context, id int64) error {
|
|||
return errors.Wrap(err, "Failed to count the child paths of the space")
|
||||
} else if count > 0 {
|
||||
// TODO: still returns 500
|
||||
return errs.SpaceWithChildsCantBeDeleted
|
||||
return store.ErrSpaceWithChildsCantBeDeleted
|
||||
}
|
||||
|
||||
// delete all paths
|
||||
|
@ -221,11 +220,11 @@ func (s *SpaceStore) Delete(ctx context.Context, id int64) error {
|
|||
|
||||
// delete the space
|
||||
if _, err := tx.Exec(spaceDelete, id); err != nil {
|
||||
return wrapSqlErrorf(err, "The delete query failed")
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -236,7 +235,7 @@ func (s *SpaceStore) Count(ctx context.Context, id int64) (int64, error) {
|
|||
var count int64
|
||||
err := s.db.QueryRowContext(ctx, spaceCount, id).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -251,7 +250,7 @@ func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter
|
|||
if opts.Sort == enum.SpaceAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, spaceSelect, id, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -287,7 +286,7 @@ func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -312,7 +311,7 @@ func (s *SpaceStore) CreatePath(ctx context.Context, spaceId int64, params *type
|
|||
Updated: params.Updated,
|
||||
}
|
||||
|
||||
return p, CreatePath(ctx, s.db, p)
|
||||
return p, CreateAliasPath(ctx, s.db, p)
|
||||
}
|
||||
|
||||
// Delete an alias of a space.
|
||||
|
|
|
@ -33,7 +33,7 @@ type UserStore struct {
|
|||
func (s *UserStore) Find(ctx context.Context, id int64) (*types.User, error) {
|
||||
dst := new(types.User)
|
||||
if err := s.db.GetContext(ctx, dst, userSelectID, id); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select by id query failed")
|
||||
return nil, processSqlErrorf(err, "Select by id query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func (s *UserStore) Find(ctx context.Context, id int64) (*types.User, error) {
|
|||
func (s *UserStore) FindEmail(ctx context.Context, email string) (*types.User, error) {
|
||||
dst := new(types.User)
|
||||
if err := s.db.GetContext(ctx, dst, userSelectEmail, email); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Select by email query failed")
|
||||
return nil, processSqlErrorf(err, "Select by email query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
if opts.Sort == enum.UserAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, userSelect, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, wrapSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -106,11 +106,11 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
func (s *UserStore) Create(ctx context.Context, user *types.User) error {
|
||||
query, arg, err := s.db.BindNamed(userInsert, user)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind user object")
|
||||
return processSqlErrorf(err, "Failed to bind user object")
|
||||
}
|
||||
|
||||
if err = s.db.QueryRowContext(ctx, query, arg...).Scan(&user.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "Insert query failed")
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -120,11 +120,11 @@ func (s *UserStore) Create(ctx context.Context, user *types.User) error {
|
|||
func (s *UserStore) Update(ctx context.Context, user *types.User) error {
|
||||
query, arg, err := s.db.BindNamed(userUpdate, user)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to bind user object")
|
||||
return processSqlErrorf(err, "Failed to bind user object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
return wrapSqlErrorf(err, "Update query failed")
|
||||
return processSqlErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return err
|
||||
|
@ -134,12 +134,12 @@ func (s *UserStore) Update(ctx context.Context, user *types.User) error {
|
|||
func (s *UserStore) Delete(ctx context.Context, user *types.User) error {
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return wrapSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer tx.Rollback()
|
||||
// delete the user
|
||||
if _, err := tx.ExecContext(ctx, userDelete, user.ID); err != nil {
|
||||
return wrapSqlErrorf(err, "The delete query failed")
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ func (s *UserStore) Count(ctx context.Context) (int64, error) {
|
|||
var count int64
|
||||
err := s.db.QueryRowContext(ctx, userCount).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, wrapSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -237,18 +236,18 @@ func testUserUpdate(store store.UserStore) func(t *testing.T) {
|
|||
// this test deletes an user from the database and then confirms
|
||||
// subsequent attempts to fetch the deleted user result in
|
||||
// a sql.ErrNoRows error.
|
||||
func testUserDelete(store store.UserStore) func(t *testing.T) {
|
||||
func testUserDelete(s store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
v, err := store.Find(noContext, 1)
|
||||
v, err := s.Find(noContext, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if err := store.Delete(noContext, v); err != nil {
|
||||
if err := s.Delete(noContext, v); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if _, err := store.Find(noContext, 1); err != sql.ErrNoRows {
|
||||
if _, err := s.Find(noContext, 1); err != store.ErrResourceNotFound {
|
||||
t.Errorf("Expected sql.ErrNoRows got %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ package database
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/internal/errs"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// default query range limit.
|
||||
|
@ -35,14 +37,19 @@ func offset(page, size int) int {
|
|||
return page * size
|
||||
}
|
||||
|
||||
func wrapSqlErrorf(original error, format string, args ...interface{}) error {
|
||||
if original == sql.ErrNoRows {
|
||||
original = errs.WrapInResourceNotFound(original)
|
||||
} else if isSqlUniqueConstraintError(original) {
|
||||
original = errs.WrapInDuplicate(original)
|
||||
// Logs the error and message, returns either the original error or a store equivalent if possible.
|
||||
func processSqlErrorf(err error, format string, args ...interface{}) error {
|
||||
// always log DB error (print formated message)
|
||||
log.Warn().Msgf("%s %s", fmt.Sprintf(format, args...), err)
|
||||
|
||||
// If it's a known error, return converted error instead.
|
||||
if err == sql.ErrNoRows {
|
||||
return store.ErrResourceNotFound
|
||||
} else if isSqlUniqueConstraintError(err) {
|
||||
return store.ErrDuplicate
|
||||
}
|
||||
|
||||
return errors.Wrapf(original, format, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func isSqlUniqueConstraintError(original error) bool {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package store
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrResourceNotFound = errors.New("Resource not found")
|
||||
ErrDuplicate = errors.New("Resource is a duplicate")
|
||||
ErrPathTooLong = errors.New("The path is too long")
|
||||
ErrPrimaryPathAlreadyExists = errors.New("Primary path already exists for resource.")
|
||||
ErrPrimaryPathRequired = errors.New("Path has to be primary.")
|
||||
ErrAliasPathRequired = errors.New("Path has to be an alias.")
|
||||
ErrPrimaryPathCantBeDeleted = errors.New("Primary path can't be deleted.")
|
||||
ErrNoChangeInRequestedMove = errors.New(("The requested move doesn't change anything."))
|
||||
ErrIllegalMoveCyclicHierarchy = errors.New(("The requested move is not permitted as it would cause a cyclic depdency."))
|
||||
ErrSpaceWithChildsCantBeDeleted = errors.New("The space can't be deleted as it still contains spaces or repos.")
|
||||
)
|
|
@ -20,11 +20,11 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrNameLength = fmt.Errorf("Name has to be between %d and %d in length.", minNameLength, maxNameLength)
|
||||
ErrNameRegex = fmt.Errorf("Name has start with a letter and only contain the following [a-z0-9-_].")
|
||||
ErrNameLength = &CheckError{fmt.Sprintf("Name has to be between %d and %d in length.", minNameLength, maxNameLength)}
|
||||
ErrNameRegex = &CheckError{fmt.Sprintf("Name has start with a letter and only contain the following [a-z0-9-_].")}
|
||||
|
||||
ErrDisplayNameLength = fmt.Errorf("Display name has to be between %d and %d in length.", minDisplayNameLength, maxDisplayNameLength)
|
||||
ErrDisplayNameRegex = fmt.Errorf("Display name has start with a letter and only contain the following [a-zA-Z0-9-_ ].")
|
||||
ErrDisplayNameLength = &CheckError{fmt.Sprintf("Display name has to be between %d and %d in length.", minDisplayNameLength, maxDisplayNameLength)}
|
||||
ErrDisplayNameRegex = &CheckError{fmt.Sprintf("Display name has start with a letter and only contain the following [a-zA-Z0-9-_ ].")}
|
||||
)
|
||||
|
||||
// Name checks the provided name and returns an error in it isn't valid.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package check
|
||||
|
||||
var (
|
||||
ErrAny = &CheckError{}
|
||||
)
|
||||
|
||||
/*
|
||||
* An error returned by check methods for any validation errors
|
||||
* WARNING: This error will be printed to the user as is!
|
||||
*/
|
||||
type CheckError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *CheckError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e *CheckError) Is(target error) bool {
|
||||
// If the caller is checking for any CheckError, return true
|
||||
if target == ErrAny {
|
||||
return true
|
||||
}
|
||||
|
||||
// ensure it's the correct type
|
||||
v, ok := target.(*CheckError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// only the same if the message is the same
|
||||
return e.msg == v.msg
|
||||
}
|
|
@ -19,18 +19,20 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrPathEmpty = fmt.Errorf("Path can't be empty.")
|
||||
ErrPathInvalidSize = fmt.Errorf("A path has to be between %d and %d segments long (%d for spaces).", minPathSegments, maxPathSegments, maxPathSegmentsForSpace)
|
||||
ErrEmptyPathSegment = fmt.Errorf("Empty segments are not allowed.")
|
||||
ErrPathCantBeginOrEndWithSeparator = fmt.Errorf("Path can't start or end with the separator ('%s').", types.PathSeparator)
|
||||
ErrPathEmpty = &CheckError{"Path can't be empty."}
|
||||
ErrPathInvalidSize = &CheckError{fmt.Sprintf("A path has to be between %d and %d segments long (%d for spaces).", minPathSegments, maxPathSegments, maxPathSegmentsForSpace)}
|
||||
ErrEmptyPathSegment = &CheckError{"Empty segments are not allowed."}
|
||||
ErrPathCantBeginOrEndWithSeparator = &CheckError{fmt.Sprintf("Path can't start or end with the separator ('%s').", types.PathSeparator)}
|
||||
ErrPathDifferentTopLevelSpace = &CheckError{"Alias paths have to stay within the same top level space."}
|
||||
ErrTopLevelPathNotAllowed = &CheckError{"Top level alias paths are not allowed."}
|
||||
)
|
||||
|
||||
/*
|
||||
* PathParams checks the provided path params and returns an error in it isn't valid.
|
||||
* Path checks the provided path and returns an error in it isn't valid.
|
||||
*
|
||||
* NOTE: A repository path can be one deeper than a space path (as otherwise the space would be useless)
|
||||
*/
|
||||
func PathParams(path string, isSpace bool) error {
|
||||
func Path(path string, isSpace bool) error {
|
||||
|
||||
if path == "" {
|
||||
return ErrPathEmpty
|
||||
|
@ -60,6 +62,37 @@ func PathParams(path string, isSpace bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates a PathParams object that is used to create a new path.
|
||||
*
|
||||
* NOTES:
|
||||
* - We don't allow top level alias paths
|
||||
* - An alias path has to stay within the same top level space
|
||||
*
|
||||
* IMPORTANT:
|
||||
* Technically there can be a racing condition when a space is being moved inbetween the validation and path creation.
|
||||
* But that is fine, as the path could've also been created a second earlier when it was still valid and would then still exist.
|
||||
*/
|
||||
func PathParams(path *types.PathParams, currentPath string, isSpace bool) error {
|
||||
// ensure the path is valid
|
||||
if err := Path(path.Path, isSpace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ensure the path is at least 1 level deep (at least one '/')
|
||||
i := strings.Index(path.Path, types.PathSeparator)
|
||||
if i < 0 {
|
||||
return ErrTopLevelPathNotAllowed
|
||||
}
|
||||
|
||||
// ensure the top level space doesn't change (add path separator to avoid abcd -> abc matching)
|
||||
if !strings.HasPrefix(currentPath+types.PathSeparator, path.Path[:i]+types.PathSeparator) {
|
||||
return ErrPathDifferentTopLevelSpace
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the provided path is too long.
|
||||
*
|
||||
|
|
|
@ -5,13 +5,11 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRepositoryRequiresSpaceId = fmt.Errorf("SpaceId required - Repositories don't exist outside of a space.")
|
||||
ErrRepositoryRequiresSpaceId = &CheckError{"SpaceId required - Repositories don't exist outside of a space."}
|
||||
)
|
||||
|
||||
// Repo checks the provided repository and returns an error in it isn't valid.
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
var (
|
||||
illegalRootSpaceNames = []string{"api"}
|
||||
|
||||
ErrRootSpaceNameNotAllowed = fmt.Errorf("The following names are not allowed for a root space: %v", illegalRootSpaceNames)
|
||||
ErrInvalidParentSpaceId = fmt.Errorf("Parent space ID has to be either zero for a root space or greater than zero for a child space.")
|
||||
ErrRootSpaceNameNotAllowed = &CheckError{fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames)}
|
||||
ErrInvalidParentSpaceId = &CheckError{"Parent space ID has to be either zero for a root space or greater than zero for a child space."}
|
||||
)
|
||||
|
||||
// Repo checks the provided space and returns an error in it isn't valid.
|
||||
|
|
|
@ -18,7 +18,7 @@ const (
|
|||
var (
|
||||
// ErrEmailLen is returned when the email address
|
||||
// exceeds the maximum number of characters.
|
||||
ErrEmailLen = fmt.Errorf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength)
|
||||
ErrEmailLen = &CheckError{fmt.Sprintf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength)}
|
||||
)
|
||||
|
||||
// User returns true if the User if valid.
|
||||
|
|
|
@ -14,7 +14,7 @@ type Repository struct {
|
|||
ID int64 `db:"repo_id" json:"id"`
|
||||
Name string `db:"repo_name" json:"name"`
|
||||
SpaceId int64 `db:"repo_spaceId" json:"spaceId"`
|
||||
Path string `db:"repo_path" json:"path"`
|
||||
Path string `db:"repo_path" json:"path"`
|
||||
DisplayName string `db:"repo_displayName" json:"displayName"`
|
||||
Description string `db:"repo_description" json:"description"`
|
||||
IsPublic bool `db:"repo_isPublic" json:"isPublic"`
|
||||
|
|
Loading…
Reference in New Issue