Skip to content

Commit f27c17b

Browse files
gateway: add user project stars api
add api to create, get, delete user project stars.
1 parent 924ef90 commit f27c17b

File tree

8 files changed

+682
-0
lines changed

8 files changed

+682
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2019 Sorint.lab
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package action
16+
17+
import (
18+
"context"
19+
20+
"github.com/sorintlab/errors"
21+
22+
"agola.io/agola/internal/services/gateway/common"
23+
"agola.io/agola/internal/util"
24+
csapitypes "agola.io/agola/services/configstore/api/types"
25+
"agola.io/agola/services/configstore/client"
26+
cstypes "agola.io/agola/services/configstore/types"
27+
)
28+
29+
type CreateUserProjectStarRequest struct {
30+
ProjectRef string
31+
}
32+
33+
func (h *ActionHandler) CreateUserProjectStar(ctx context.Context, req *CreateUserProjectStarRequest) (*cstypes.ProjectStar, error) {
34+
if !common.IsUserLogged(ctx) {
35+
return nil, errors.Errorf("user not logged in")
36+
}
37+
38+
userID := common.CurrentUserID(ctx)
39+
40+
creq := &csapitypes.CreateProjectStarRequest{
41+
UserRef: userID,
42+
ProjectRef: req.ProjectRef,
43+
}
44+
45+
projectStar, _, err := h.configstoreClient.CreateProjectStar(ctx, creq)
46+
if err != nil {
47+
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to create project star"))
48+
}
49+
50+
return projectStar, nil
51+
}
52+
53+
func (h *ActionHandler) DeleteUserProjectStar(ctx context.Context, projectRef string) error {
54+
if !common.IsUserLogged(ctx) {
55+
return errors.Errorf("user not logged in")
56+
}
57+
58+
userID := common.CurrentUserID(ctx)
59+
60+
if _, err := h.configstoreClient.DeleteProjectStar(ctx, userID, projectRef); err != nil {
61+
return util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to delete project star"))
62+
}
63+
return nil
64+
}
65+
66+
type GetUserProjectStarsRequest struct {
67+
Cursor string
68+
69+
Limit int
70+
SortDirection SortDirection
71+
}
72+
73+
type GetUserProjectStarsResponse struct {
74+
ProjectStars []*cstypes.ProjectStar
75+
Cursor string
76+
}
77+
78+
func (h *ActionHandler) GetUserProjectStars(ctx context.Context, req *GetUserProjectStarsRequest) (*GetUserProjectStarsResponse, error) {
79+
if !common.IsUserLogged(ctx) {
80+
return nil, errors.Errorf("user not logged in")
81+
}
82+
userID := common.CurrentUserID(ctx)
83+
84+
inCursor := &StartCursor{}
85+
sortDirection := req.SortDirection
86+
if req.Cursor != "" {
87+
if err := UnmarshalCursor(req.Cursor, inCursor); err != nil {
88+
return nil, errors.WithStack(err)
89+
}
90+
sortDirection = inCursor.SortDirection
91+
}
92+
if sortDirection == "" {
93+
sortDirection = SortDirectionAsc
94+
}
95+
96+
projectStars, resp, err := h.configstoreClient.GetProjectStars(ctx, userID, &client.GetProjectStarsOptions{ListOptions: &client.ListOptions{Limit: req.Limit, SortDirection: cstypes.SortDirection(sortDirection)}, StartProjectStarID: inCursor.Start})
97+
if err != nil {
98+
return nil, util.NewAPIError(util.KindFromRemoteError(err), err)
99+
}
100+
101+
var outCursor string
102+
if resp.HasMore && len(projectStars) > 0 {
103+
lastProjectStarID := projectStars[len(projectStars)-1].ID
104+
outCursor, err = MarshalCursor(&StartCursor{
105+
Start: lastProjectStarID,
106+
SortDirection: sortDirection,
107+
})
108+
if err != nil {
109+
return nil, errors.WithStack(err)
110+
}
111+
}
112+
113+
res := &GetUserProjectStarsResponse{
114+
ProjectStars: projectStars,
115+
Cursor: outCursor,
116+
}
117+
118+
return res, nil
119+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2024 Sorint.lab
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package api
16+
17+
import (
18+
"net/http"
19+
20+
"github.com/gorilla/mux"
21+
"github.com/rs/zerolog"
22+
"github.com/sorintlab/errors"
23+
24+
"agola.io/agola/internal/services/gateway/action"
25+
util "agola.io/agola/internal/util"
26+
cstypes "agola.io/agola/services/configstore/types"
27+
gwapitypes "agola.io/agola/services/gateway/api/types"
28+
)
29+
30+
type CreateUserProjectStarHandler struct {
31+
log zerolog.Logger
32+
ah *action.ActionHandler
33+
}
34+
35+
func NewCreateUserProjectStarHandler(log zerolog.Logger, ah *action.ActionHandler) *CreateUserProjectStarHandler {
36+
return &CreateUserProjectStarHandler{log: log, ah: ah}
37+
}
38+
39+
func (h *CreateUserProjectStarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
40+
ctx := r.Context()
41+
vars := mux.Vars(r)
42+
43+
projectRef := vars["projectref"]
44+
45+
creq := &action.CreateUserProjectStarRequest{
46+
ProjectRef: projectRef,
47+
}
48+
49+
projectStar, err := h.ah.CreateUserProjectStar(ctx, creq)
50+
if util.HTTPError(w, err) {
51+
h.log.Err(err).Send()
52+
return
53+
}
54+
55+
if err := util.HTTPResponse(w, http.StatusCreated, projectStar); err != nil {
56+
h.log.Err(err).Send()
57+
}
58+
}
59+
60+
type DeleteUserProjectStarHandler struct {
61+
log zerolog.Logger
62+
ah *action.ActionHandler
63+
}
64+
65+
func NewDeleteUserProjectStarHandler(log zerolog.Logger, ah *action.ActionHandler) *DeleteUserProjectStarHandler {
66+
return &DeleteUserProjectStarHandler{log: log, ah: ah}
67+
}
68+
69+
func (h *DeleteUserProjectStarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
70+
ctx := r.Context()
71+
vars := mux.Vars(r)
72+
73+
projectRef := vars["projectref"]
74+
75+
err := h.ah.DeleteUserProjectStar(ctx, projectRef)
76+
if util.HTTPError(w, err) {
77+
h.log.Err(err).Send()
78+
return
79+
}
80+
81+
if err := util.HTTPResponse(w, http.StatusNoContent, nil); err != nil {
82+
h.log.Err(err).Send()
83+
}
84+
}
85+
86+
type UserProjectStarsHandler struct {
87+
log zerolog.Logger
88+
ah *action.ActionHandler
89+
}
90+
91+
func NewUserProjectStarsHandler(log zerolog.Logger, ah *action.ActionHandler) *UserProjectStarsHandler {
92+
return &UserProjectStarsHandler{log: log, ah: ah}
93+
}
94+
95+
func (h *UserProjectStarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
96+
res, err := h.do(w, r)
97+
if util.HTTPError(w, err) {
98+
h.log.Err(err).Send()
99+
return
100+
}
101+
102+
if err := util.HTTPResponse(w, http.StatusOK, res); err != nil {
103+
h.log.Err(err).Send()
104+
}
105+
}
106+
107+
func (h *UserProjectStarsHandler) do(w http.ResponseWriter, r *http.Request) ([]*gwapitypes.ProjectStarResponse, error) {
108+
ctx := r.Context()
109+
110+
ropts, err := parseRequestOptions(r)
111+
if err != nil {
112+
return nil, errors.WithStack(err)
113+
}
114+
115+
ares, err := h.ah.GetUserProjectStars(ctx, &action.GetUserProjectStarsRequest{Cursor: ropts.Cursor, Limit: ropts.Limit, SortDirection: action.SortDirection(ropts.SortDirection)})
116+
if err != nil {
117+
return nil, errors.WithStack(err)
118+
}
119+
120+
projectStars := make([]*gwapitypes.ProjectStarResponse, len(ares.ProjectStars))
121+
for i, p := range ares.ProjectStars {
122+
projectStars[i] = createProjectStarResponse(p)
123+
}
124+
125+
addCursorHeader(w, ares.Cursor)
126+
127+
return projectStars, nil
128+
}
129+
130+
func createProjectStarResponse(o *cstypes.ProjectStar) *gwapitypes.ProjectStarResponse {
131+
org := &gwapitypes.ProjectStarResponse{
132+
ID: o.ID,
133+
ProjectID: o.ProjectID,
134+
}
135+
return org
136+
}

internal/services/gateway/gateway.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ func (g *Gateway) Run(ctx context.Context) error {
265265
userRunLogsHandler := api.NewLogsHandler(g.log, g.ah, scommon.GroupTypeUser)
266266
userRunLogsDeleteHandler := api.NewLogsDeleteHandler(g.log, g.ah, scommon.GroupTypeUser)
267267

268+
createUserProjectStarHandler := api.NewCreateUserProjectStarHandler(g.log, g.ah)
269+
deleteUserProjectStarHandler := api.NewDeleteUserProjectStarHandler(g.log, g.ah)
270+
userProjectStarsHandler := api.NewUserProjectStarsHandler(g.log, g.ah)
271+
268272
userRemoteReposHandler := api.NewUserRemoteReposHandler(g.log, g.ah, g.configstoreClient)
269273

270274
badgeHandler := api.NewBadgeHandler(g.log, g.ah)
@@ -354,6 +358,9 @@ func (g *Gateway) Run(ctx context.Context) error {
354358
apirouter.Handle("/user/orgs", authForcedHandler(userOrgsHandler)).Methods("GET")
355359
apirouter.Handle("/user/org_invitations", authForcedHandler(userOrgInvitationsHandler)).Methods("GET")
356360
apirouter.Handle("/user/org_invitations/{orgref}/actions", authForcedHandler(userOrgInvitationActionHandler)).Methods("PUT")
361+
apirouter.Handle("/user/projects/{projectref}/projectstars", authForcedHandler(createUserProjectStarHandler)).Methods("POST")
362+
apirouter.Handle("/user/projects/{projectref}/projectstars", authForcedHandler(deleteUserProjectStarHandler)).Methods("DELETE")
363+
apirouter.Handle("/user/projectstars", authForcedHandler(userProjectStarsHandler)).Methods("GET")
357364

358365
apirouter.Handle("/users/{userref}/runs", authForcedHandler(userRunsHandler)).Methods("GET")
359366
apirouter.Handle("/users/{userref}/runs/{runnumber}", authOptionalHandler(userRunHandler)).Methods("GET")
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2024 Sorint.lab
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package types
16+
17+
type CreateProjectStarRequest struct {
18+
UserRef string
19+
ProjectRef string
20+
}

services/configstore/client/client.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,3 +778,42 @@ func (c *Client) Import(ctx context.Context, r io.Reader) (*Response, error) {
778778
resp, err := c.GetResponse(ctx, "POST", "/import", nil, -1, common.JSONContent, r)
779779
return resp, errors.WithStack(err)
780780
}
781+
782+
func (c *Client) CreateProjectStar(ctx context.Context, req *csapitypes.CreateProjectStarRequest) (*cstypes.ProjectStar, *Response, error) {
783+
reqj, err := json.Marshal(req)
784+
if err != nil {
785+
return nil, nil, errors.WithStack(err)
786+
}
787+
788+
projectStar := new(cstypes.ProjectStar)
789+
resp, err := c.GetParsedResponse(ctx, "POST", fmt.Sprintf("/users/%s/projects/%s/projectstars", req.UserRef, req.ProjectRef), nil, common.JSONContent, bytes.NewReader(reqj), projectStar)
790+
return projectStar, resp, errors.WithStack(err)
791+
}
792+
793+
func (c *Client) DeleteProjectStar(ctx context.Context, userRef string, projectRef string) (*Response, error) {
794+
resp, err := c.GetResponse(ctx, "DELETE", fmt.Sprintf("/users/%s/projects/%s/projectstars", userRef, projectRef), nil, -1, common.JSONContent, nil)
795+
return resp, errors.WithStack(err)
796+
}
797+
798+
type GetProjectStarsOptions struct {
799+
*ListOptions
800+
801+
StartProjectStarID string
802+
}
803+
804+
func (o *GetProjectStarsOptions) Add(q url.Values) {
805+
o.ListOptions.Add(q)
806+
807+
if o.StartProjectStarID != "" {
808+
q.Add("startprojectstarid", o.StartProjectStarID)
809+
}
810+
}
811+
812+
func (c *Client) GetProjectStars(ctx context.Context, userRef string, opts *GetProjectStarsOptions) ([]*cstypes.ProjectStar, *Response, error) {
813+
q := url.Values{}
814+
opts.Add(q)
815+
816+
projectStars := []*cstypes.ProjectStar{}
817+
resp, err := c.GetParsedResponse(ctx, "GET", fmt.Sprintf("/users/%s/projectstars", userRef), q, common.JSONContent, nil, &projectStars)
818+
return projectStars, resp, errors.WithStack(err)
819+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package types
2+
3+
type ProjectStarResponse struct {
4+
ID string `json:"id"`
5+
ProjectID string `json:"project_id"`
6+
}
7+
8+
type CreateProjectStarRequest struct {
9+
ProjectRef string
10+
}

services/gateway/client/client.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,3 +871,27 @@ func (c *Client) GetProjectCommitStatusDeliveries(ctx context.Context, projectRe
871871
func (c *Client) ProjectCommitStatusRedelivery(ctx context.Context, projectRef string, commitStatusDeliveryID string) (*Response, error) {
872872
return c.getResponse(ctx, "PUT", fmt.Sprintf("/projects/%s/commitstatusdeliveries/%s/redelivery", projectRef, commitStatusDeliveryID), nil, jsonContent, nil)
873873
}
874+
875+
func (c *Client) GetUserProjectStars(ctx context.Context, opts *ListOptions) ([]*gwapitypes.ProjectStarResponse, *Response, error) {
876+
q := url.Values{}
877+
opts.Add(q)
878+
879+
orgs := []*gwapitypes.ProjectStarResponse{}
880+
resp, err := c.getParsedResponse(ctx, "GET", "/user/projectstars", q, jsonContent, nil, &orgs)
881+
return orgs, resp, errors.WithStack(err)
882+
}
883+
884+
func (c *Client) CreateUserProjectStar(ctx context.Context, req *gwapitypes.CreateProjectStarRequest) (*gwapitypes.ProjectStarResponse, *Response, error) {
885+
reqj, err := json.Marshal(req)
886+
if err != nil {
887+
return nil, nil, errors.WithStack(err)
888+
}
889+
890+
projectStar := new(gwapitypes.ProjectStarResponse)
891+
resp, err := c.getParsedResponse(ctx, "POST", fmt.Sprintf("/user/projects/%s/projectstars", req.ProjectRef), nil, jsonContent, bytes.NewReader(reqj), projectStar)
892+
return projectStar, resp, errors.WithStack(err)
893+
}
894+
895+
func (c *Client) DeleteUserProjectStar(ctx context.Context, projectRef string) (*Response, error) {
896+
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/user/projects/%s/projectstars", projectRef), nil, jsonContent, nil)
897+
}

0 commit comments

Comments
 (0)