Skip to content

Commit

Permalink
Gateway: added api to get user projects
Browse files Browse the repository at this point in the history
  • Loading branch information
alessandro-sorint committed Jun 21, 2022
1 parent 5560502 commit 8269265
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 0 deletions.
84 changes: 84 additions & 0 deletions internal/services/gateway/action/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -997,3 +998,86 @@ func (h *ActionHandler) UserCreateRun(ctx context.Context, req *UserCreateRunReq

return h.CreateRuns(ctx, creq)
}

type GetUserProjectsRequest struct {
UserRef string
Limit int
Page int
}

func ProjectsPaginate(page int, limit int, data []*csapitypes.Project) []*csapitypes.Project {
start := page * limit

if start > len(data) {
return nil
}

end := start + limit
if end > len(data) {
end = len(data)
}

return data[start:end]
}

func (h *ActionHandler) GetUserProjects(ctx context.Context, req *GetUserProjectsRequest) ([]*csapitypes.Project, error) {
if !common.IsUserLogged(ctx) {
return nil, errors.Errorf("user not logged in")
}
user, _, err := h.configstoreClient.GetUser(ctx, req.UserRef)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get user %q", req.UserRef))
}

projects := make([]*csapitypes.Project, 0)

prj, err := h.GetProjectgroupProjects(ctx, "user"+"/"+user.Name)
if err != nil {
return nil, errors.Wrapf(err, "failed to get projects for user %q", user.Name)
}
projects = append(projects, prj...)

userOrgs, err := h.GetUserOrgs(ctx, req.UserRef)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get organizations for user %q", req.UserRef))
}
for _, org := range userOrgs {
prj, err := h.GetProjectgroupProjects(ctx, "org"+"/"+org.Organization.Name)
if err != nil {
return nil, errors.Wrapf(err, "failed to get projects for org %q", org.Organization.Name)
}
projects = append(projects, prj...)
}

sort.SliceStable(projects, func(i, j int) bool {
return strings.Compare(strings.ToLower(projects[i].Path), strings.ToLower(projects[j].Path)) < 0
})

projects = ProjectsPaginate(req.Page, req.Limit, projects)

return projects, nil
}

func (h *ActionHandler) GetProjectgroupProjects(ctx context.Context, projectgroup string) ([]*csapitypes.Project, error) {
projects := make([]*csapitypes.Project, 0)

prj, err := h.GetProjectGroupProjects(ctx, projectgroup)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get projects for group %q", projectgroup))
}
projects = append(projects, prj...)

subgroups, err := h.GetProjectGroupSubgroups(ctx, projectgroup)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get subgroups for group %q", projectgroup))
}
for _, s := range subgroups {
prj, err := h.GetProjectgroupProjects(ctx, s.ID)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get projects for group %q", s.ID))
}
projects = append(projects, prj...)
}

return projects, nil
}
78 changes: 78 additions & 0 deletions internal/services/gateway/api/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import (
"github.com/rs/zerolog"
)

const (
DefaultProjectsLimit = 25
MaxProjectsLimit = 40
)

type CreateUserHandler struct {
log zerolog.Logger
ah *action.ActionHandler
Expand Down Expand Up @@ -643,3 +648,76 @@ func createUserOrgsResponse(o *csapitypes.UserOrgsResponse) *gwapitypes.UserOrgs

return userOrgs
}

type UserProjectsHandler struct {
log zerolog.Logger
ah *action.ActionHandler
}

func NewUserProjectsHandler(log zerolog.Logger, ah *action.ActionHandler) *UserProjectsHandler {
return &UserProjectsHandler{log: log, ah: ah}
}

func (h *UserProjectsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

userID := common.CurrentUserID(ctx)
if userID == "" {
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("user not authenticated")))
return
}

q := r.URL.Query()

limitS := q.Get("limit")
limit := DefaultProjectsLimit
if limitS != "" {
var err error
limit, err = strconv.Atoi(limitS)
if err != nil {
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Wrapf(err, "cannot parse limit")))
return
}
}
if limit < 0 {
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("limit must be greater or equal than 0")))
return
}
if limit > MaxProjectsLimit {
limit = MaxRunsLimit
}

pageS := q.Get("page")
page := 0
if pageS != "" {
var err error
page, err = strconv.Atoi(pageS)
if err != nil {
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Wrapf(err, "cannot parse page")))
return
}
}
if page < 0 {
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("page must be greater or equal than 0")))
return
}

userProjects, err := h.ah.GetUserProjects(ctx, &action.GetUserProjectsRequest{
UserRef: userID,
Limit: limit,
Page: page,
})
if util.HTTPError(w, err) {
h.log.Err(err).Send()
return
}

res := make([]*gwapitypes.ProjectResponse, len(userProjects))
for i, userProject := range userProjects {
res[i] = createProjectResponse(userProject)
}

if err := util.HTTPResponse(w, http.StatusOK, res); err != nil {
h.log.Err(err).Send()
}
}
2 changes: 2 additions & 0 deletions internal/services/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func (g *Gateway) Run(ctx context.Context) error {
deleteUserHandler := api.NewDeleteUserHandler(g.log, g.ah)
userCreateRunHandler := api.NewUserCreateRunHandler(g.log, g.ah)
userOrgsHandler := api.NewUserOrgsHandler(g.log, g.ah)
userProjectsHandler := api.NewUserProjectsHandler(g.log, g.ah)

createUserLAHandler := api.NewCreateUserLAHandler(g.log, g.ah)
deleteUserLAHandler := api.NewDeleteUserLAHandler(g.log, g.ah)
Expand Down Expand Up @@ -289,6 +290,7 @@ func (g *Gateway) Run(ctx context.Context) error {
apirouter.Handle("/users/{userref}", authForcedHandler(deleteUserHandler)).Methods("DELETE")
apirouter.Handle("/user/createrun", authForcedHandler(userCreateRunHandler)).Methods("POST")
apirouter.Handle("/user/orgs", authForcedHandler(userOrgsHandler)).Methods("GET")
apirouter.Handle("/user/projects", authForcedHandler(userProjectsHandler)).Methods("GET")

apirouter.Handle("/users/{userref}/runs", authForcedHandler(userRunsHandler)).Methods("GET")
apirouter.Handle("/users/{userref}/runs/{runnumber}", authOptionalHandler(userRunHandler)).Methods("GET")
Expand Down
15 changes: 15 additions & 0 deletions services/gateway/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,18 @@ func (c *Client) GetUserOrgs(ctx context.Context) ([]*gwapitypes.UserOrgsRespons
resp, err := c.getParsedResponse(ctx, "GET", "/user/orgs", nil, jsonContent, nil, &userOrgs)
return userOrgs, resp, errors.WithStack(err)
}

func (c *Client) GetUserProjects(ctx context.Context, page int, limit int) ([]*gwapitypes.ProjectResponse, *http.Response, error) {
userProjects := []*gwapitypes.ProjectResponse{}

q := url.Values{}
if page > 0 {
q.Add("page", strconv.Itoa(page))
}
if limit > 0 {
q.Add("limit", strconv.Itoa(limit))
}

resp, err := c.getParsedResponse(ctx, "GET", "/user/projects", q, jsonContent, nil, &userProjects)
return userProjects, resp, errors.WithStack(err)
}
63 changes: 63 additions & 0 deletions tests/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1928,3 +1928,66 @@ func TestUserOrgs(t *testing.T) {
t.Fatalf("user orgs mismatch (-want +got):\n%s", diff)
}
}

func TestUserProjects(t *testing.T) {
dir := t.TempDir()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

tgitea, c := setup(ctx, t, dir, true)
defer shutdownGitea(tgitea)

giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)

giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)

giteaRepo, err := giteaClient.CreateRepo(gitea.CreateRepoOption{
Name: "repo01",
Private: false,
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
t.Logf("created gitea repo: %s", giteaRepo.Name)

gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)

project01, _, err := gwClient.CreateProject(ctx, &gwapitypes.CreateProjectRequest{
Name: "Project01",
ParentRef: path.Join("user", agolaUser01),
RemoteSourceName: "gitea",
RepoPath: path.Join(giteaUser01, "repo01"),
Visibility: gwapitypes.VisibilityPublic,
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

_, _, err = gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org02", Visibility: gwapitypes.VisibilityPublic})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
project02, _, err := gwClient.CreateProject(ctx, &gwapitypes.CreateProjectRequest{
Name: "Project02",
ParentRef: path.Join("org", "org02"),
RemoteSourceName: "gitea",
RepoPath: path.Join(giteaUser01, "repo01"),
Visibility: gwapitypes.VisibilityPublic,
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

userProjects, _, err := gwClient.GetUserProjects(ctx, 0, 10)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

expectedProjects := []*gwapitypes.ProjectResponse{project02, project01}

if diff := cmp.Diff(expectedProjects, userProjects); diff != "" {
t.Fatalf("user projects mismatch (-want +got):\n%s", diff)
}
}

0 comments on commit 8269265

Please sign in to comment.