Skip to content

Commit 76589bd

Browse files
Gateway: added api to get user projects
1 parent 23c66d7 commit 76589bd

File tree

5 files changed

+242
-0
lines changed

5 files changed

+242
-0
lines changed

internal/services/gateway/action/user.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"encoding/json"
2020
"fmt"
2121
"regexp"
22+
"sort"
2223
"strings"
2324
"time"
2425

@@ -1027,3 +1028,86 @@ func (h *ActionHandler) GetUserGitSource(ctx context.Context, remoteSourceRef, u
10271028

10281029
return gitSource, rs, la, nil
10291030
}
1031+
1032+
type GetUserProjectsRequest struct {
1033+
UserRef string
1034+
Limit int
1035+
Page int
1036+
}
1037+
1038+
func ProjectsPaginate(page int, limit int, data []*csapitypes.Project) []*csapitypes.Project {
1039+
start := page * limit
1040+
1041+
if start > len(data) {
1042+
return nil
1043+
}
1044+
1045+
end := start + limit
1046+
if end > len(data) {
1047+
end = len(data)
1048+
}
1049+
1050+
return data[start:end]
1051+
}
1052+
1053+
func (h *ActionHandler) GetUserProjects(ctx context.Context, req *GetUserProjectsRequest) ([]*csapitypes.Project, error) {
1054+
if !common.IsUserLogged(ctx) {
1055+
return nil, errors.Errorf("user not logged in")
1056+
}
1057+
user, _, err := h.configstoreClient.GetUser(ctx, req.UserRef)
1058+
if err != nil {
1059+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get user %q", req.UserRef))
1060+
}
1061+
1062+
projects := make([]*csapitypes.Project, 0)
1063+
1064+
prj, err := h.GetProjectgroupProjects(ctx, "user"+"/"+user.Name)
1065+
if err != nil {
1066+
return nil, errors.Wrapf(err, "failed to get projects for user %q", user.Name)
1067+
}
1068+
projects = append(projects, prj...)
1069+
1070+
userOrgs, err := h.GetUserOrgs(ctx, req.UserRef)
1071+
if err != nil {
1072+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get organizations for user %q", req.UserRef))
1073+
}
1074+
for _, org := range userOrgs {
1075+
prj, err := h.GetProjectgroupProjects(ctx, "org"+"/"+org.Organization.Name)
1076+
if err != nil {
1077+
return nil, errors.Wrapf(err, "failed to get projects for org %q", org.Organization.Name)
1078+
}
1079+
projects = append(projects, prj...)
1080+
}
1081+
1082+
sort.SliceStable(projects, func(i, j int) bool {
1083+
return strings.Compare(strings.ToLower(projects[i].Path), strings.ToLower(projects[j].Path)) < 0
1084+
})
1085+
1086+
projects = ProjectsPaginate(req.Page, req.Limit, projects)
1087+
1088+
return projects, nil
1089+
}
1090+
1091+
func (h *ActionHandler) GetProjectgroupProjects(ctx context.Context, projectgroup string) ([]*csapitypes.Project, error) {
1092+
projects := make([]*csapitypes.Project, 0)
1093+
1094+
prj, err := h.GetProjectGroupProjects(ctx, projectgroup)
1095+
if err != nil {
1096+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get projects for group %q", projectgroup))
1097+
}
1098+
projects = append(projects, prj...)
1099+
1100+
subgroups, err := h.GetProjectGroupSubgroups(ctx, projectgroup)
1101+
if err != nil {
1102+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get subgroups for group %q", projectgroup))
1103+
}
1104+
for _, s := range subgroups {
1105+
prj, err := h.GetProjectgroupProjects(ctx, s.ID)
1106+
if err != nil {
1107+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to get projects for group %q", s.ID))
1108+
}
1109+
projects = append(projects, prj...)
1110+
}
1111+
1112+
return projects, nil
1113+
}

internal/services/gateway/api/user.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ import (
3333
"github.com/rs/zerolog"
3434
)
3535

36+
const (
37+
DefaultProjectsLimit = 25
38+
MaxProjectsLimit = 40
39+
)
40+
3641
type CreateUserHandler struct {
3742
log zerolog.Logger
3843
ah *action.ActionHandler
@@ -643,3 +648,76 @@ func createUserOrgsResponse(o *csapitypes.UserOrgsResponse) *gwapitypes.UserOrgs
643648

644649
return userOrgs
645650
}
651+
652+
type UserProjectsHandler struct {
653+
log zerolog.Logger
654+
ah *action.ActionHandler
655+
}
656+
657+
func NewUserProjectsHandler(log zerolog.Logger, ah *action.ActionHandler) *UserProjectsHandler {
658+
return &UserProjectsHandler{log: log, ah: ah}
659+
}
660+
661+
func (h *UserProjectsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
662+
ctx := r.Context()
663+
664+
userID := common.CurrentUserID(ctx)
665+
if userID == "" {
666+
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("user not authenticated")))
667+
return
668+
}
669+
670+
q := r.URL.Query()
671+
672+
limitS := q.Get("limit")
673+
limit := DefaultProjectsLimit
674+
if limitS != "" {
675+
var err error
676+
limit, err = strconv.Atoi(limitS)
677+
if err != nil {
678+
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Wrapf(err, "cannot parse limit")))
679+
return
680+
}
681+
}
682+
if limit < 0 {
683+
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("limit must be greater or equal than 0")))
684+
return
685+
}
686+
if limit > MaxProjectsLimit {
687+
limit = MaxRunsLimit
688+
}
689+
690+
pageS := q.Get("page")
691+
page := 0
692+
if pageS != "" {
693+
var err error
694+
page, err = strconv.Atoi(pageS)
695+
if err != nil {
696+
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Wrapf(err, "cannot parse page")))
697+
return
698+
}
699+
}
700+
if page < 0 {
701+
util.HTTPError(w, util.NewAPIError(util.ErrBadRequest, errors.Errorf("page must be greater or equal than 0")))
702+
return
703+
}
704+
705+
userProjects, err := h.ah.GetUserProjects(ctx, &action.GetUserProjectsRequest{
706+
UserRef: userID,
707+
Limit: limit,
708+
Page: page,
709+
})
710+
if util.HTTPError(w, err) {
711+
h.log.Err(err).Send()
712+
return
713+
}
714+
715+
res := make([]*gwapitypes.ProjectResponse, len(userProjects))
716+
for i, userProject := range userProjects {
717+
res[i] = createProjectResponse(userProject)
718+
}
719+
720+
if err := util.HTTPResponse(w, http.StatusOK, res); err != nil {
721+
h.log.Err(err).Send()
722+
}
723+
}

internal/services/gateway/gateway.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ func (g *Gateway) Run(ctx context.Context) error {
182182
deleteUserHandler := api.NewDeleteUserHandler(g.log, g.ah)
183183
userCreateRunHandler := api.NewUserCreateRunHandler(g.log, g.ah)
184184
userOrgsHandler := api.NewUserOrgsHandler(g.log, g.ah)
185+
userProjectsHandler := api.NewUserProjectsHandler(g.log, g.ah)
185186

186187
createUserLAHandler := api.NewCreateUserLAHandler(g.log, g.ah)
187188
deleteUserLAHandler := api.NewDeleteUserLAHandler(g.log, g.ah)
@@ -291,6 +292,7 @@ func (g *Gateway) Run(ctx context.Context) error {
291292
apirouter.Handle("/users/{userref}", authForcedHandler(deleteUserHandler)).Methods("DELETE")
292293
apirouter.Handle("/user/createrun", authForcedHandler(userCreateRunHandler)).Methods("POST")
293294
apirouter.Handle("/user/orgs", authForcedHandler(userOrgsHandler)).Methods("GET")
295+
apirouter.Handle("/user/projects", authForcedHandler(userProjectsHandler)).Methods("GET")
294296

295297
apirouter.Handle("/users/{userref}/runs", authForcedHandler(userRunsHandler)).Methods("GET")
296298
apirouter.Handle("/users/{userref}/runs/{runnumber}", authOptionalHandler(userRunHandler)).Methods("GET")

services/gateway/client/client.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,3 +643,18 @@ func (c *Client) RefreshRemoteRepo(ctx context.Context, projectRef string) (*gwa
643643
resp, err := c.getParsedResponse(ctx, "POST", path.Join("/projects", url.PathEscape(projectRef), "/refreshremoterepo"), nil, jsonContent, nil, project)
644644
return project, resp, err
645645
}
646+
647+
func (c *Client) GetUserProjects(ctx context.Context, page int, limit int) ([]*gwapitypes.ProjectResponse, *http.Response, error) {
648+
userProjects := []*gwapitypes.ProjectResponse{}
649+
650+
q := url.Values{}
651+
if page > 0 {
652+
q.Add("page", strconv.Itoa(page))
653+
}
654+
if limit > 0 {
655+
q.Add("limit", strconv.Itoa(limit))
656+
}
657+
658+
resp, err := c.getParsedResponse(ctx, "GET", "/user/projects", q, jsonContent, nil, &userProjects)
659+
return userProjects, resp, errors.WithStack(err)
660+
}

tests/setup_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,3 +2252,66 @@ func TestRefreshRemoteRepositoryInfo(t *testing.T) {
22522252
t.Fatalf("projects mismatch (-expected +got):\n%s", diff)
22532253
}
22542254
}
2255+
2256+
func TestUserProjects(t *testing.T) {
2257+
dir := t.TempDir()
2258+
2259+
ctx, cancel := context.WithCancel(context.Background())
2260+
defer cancel()
2261+
2262+
tgitea, c := setup(ctx, t, dir, true)
2263+
defer shutdownGitea(tgitea)
2264+
2265+
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
2266+
2267+
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
2268+
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
2269+
2270+
giteaRepo, err := giteaClient.CreateRepo(gitea.CreateRepoOption{
2271+
Name: "repo01",
2272+
Private: false,
2273+
})
2274+
if err != nil {
2275+
t.Fatalf("unexpected err: %v", err)
2276+
}
2277+
t.Logf("created gitea repo: %s", giteaRepo.Name)
2278+
2279+
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
2280+
2281+
project01, _, err := gwClient.CreateProject(ctx, &gwapitypes.CreateProjectRequest{
2282+
Name: "Project01",
2283+
ParentRef: path.Join("user", agolaUser01),
2284+
RemoteSourceName: "gitea",
2285+
RepoPath: path.Join(giteaUser01, "repo01"),
2286+
Visibility: gwapitypes.VisibilityPublic,
2287+
})
2288+
if err != nil {
2289+
t.Fatalf("unexpected err: %v", err)
2290+
}
2291+
2292+
_, _, err = gwClient.CreateOrg(ctx, &gwapitypes.CreateOrgRequest{Name: "org02", Visibility: gwapitypes.VisibilityPublic})
2293+
if err != nil {
2294+
t.Fatalf("unexpected err: %v", err)
2295+
}
2296+
project02, _, err := gwClient.CreateProject(ctx, &gwapitypes.CreateProjectRequest{
2297+
Name: "Project02",
2298+
ParentRef: path.Join("org", "org02"),
2299+
RemoteSourceName: "gitea",
2300+
RepoPath: path.Join(giteaUser01, "repo01"),
2301+
Visibility: gwapitypes.VisibilityPublic,
2302+
})
2303+
if err != nil {
2304+
t.Fatalf("unexpected err: %v", err)
2305+
}
2306+
2307+
userProjects, _, err := gwClient.GetUserProjects(ctx, 0, 10)
2308+
if err != nil {
2309+
t.Fatalf("unexpected err: %v", err)
2310+
}
2311+
2312+
expectedProjects := []*gwapitypes.ProjectResponse{project02, project01}
2313+
2314+
if diff := cmp.Diff(expectedProjects, userProjects); diff != "" {
2315+
t.Fatalf("user projects mismatch (-want +got):\n%s", diff)
2316+
}
2317+
}

0 commit comments

Comments
 (0)