Skip to content

Commit

Permalink
gateway: add user project stars api
Browse files Browse the repository at this point in the history
add api to create, get, delete user project stars.
  • Loading branch information
alessandro-sorint committed Mar 4, 2024
1 parent 924ef90 commit f27c17b
Show file tree
Hide file tree
Showing 8 changed files with 682 additions and 0 deletions.
119 changes: 119 additions & 0 deletions internal/services/gateway/action/projectstar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.

package action

import (
"context"

"github.com/sorintlab/errors"

"agola.io/agola/internal/services/gateway/common"
"agola.io/agola/internal/util"
csapitypes "agola.io/agola/services/configstore/api/types"
"agola.io/agola/services/configstore/client"
cstypes "agola.io/agola/services/configstore/types"
)

type CreateUserProjectStarRequest struct {
ProjectRef string
}

func (h *ActionHandler) CreateUserProjectStar(ctx context.Context, req *CreateUserProjectStarRequest) (*cstypes.ProjectStar, error) {
if !common.IsUserLogged(ctx) {
return nil, errors.Errorf("user not logged in")
}

userID := common.CurrentUserID(ctx)

creq := &csapitypes.CreateProjectStarRequest{
UserRef: userID,
ProjectRef: req.ProjectRef,
}

projectStar, _, err := h.configstoreClient.CreateProjectStar(ctx, creq)
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to create project star"))
}

return projectStar, nil
}

func (h *ActionHandler) DeleteUserProjectStar(ctx context.Context, projectRef string) error {
if !common.IsUserLogged(ctx) {
return errors.Errorf("user not logged in")
}

userID := common.CurrentUserID(ctx)

if _, err := h.configstoreClient.DeleteProjectStar(ctx, userID, projectRef); err != nil {
return util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to delete project star"))
}
return nil
}

type GetUserProjectStarsRequest struct {
Cursor string

Limit int
SortDirection SortDirection
}

type GetUserProjectStarsResponse struct {
ProjectStars []*cstypes.ProjectStar
Cursor string
}

func (h *ActionHandler) GetUserProjectStars(ctx context.Context, req *GetUserProjectStarsRequest) (*GetUserProjectStarsResponse, error) {
if !common.IsUserLogged(ctx) {
return nil, errors.Errorf("user not logged in")
}
userID := common.CurrentUserID(ctx)

inCursor := &StartCursor{}
sortDirection := req.SortDirection
if req.Cursor != "" {
if err := UnmarshalCursor(req.Cursor, inCursor); err != nil {
return nil, errors.WithStack(err)
}
sortDirection = inCursor.SortDirection
}
if sortDirection == "" {
sortDirection = SortDirectionAsc
}

projectStars, resp, err := h.configstoreClient.GetProjectStars(ctx, userID, &client.GetProjectStarsOptions{ListOptions: &client.ListOptions{Limit: req.Limit, SortDirection: cstypes.SortDirection(sortDirection)}, StartProjectStarID: inCursor.Start})
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), err)
}

var outCursor string
if resp.HasMore && len(projectStars) > 0 {
lastProjectStarID := projectStars[len(projectStars)-1].ID
outCursor, err = MarshalCursor(&StartCursor{
Start: lastProjectStarID,
SortDirection: sortDirection,
})
if err != nil {
return nil, errors.WithStack(err)
}
}

res := &GetUserProjectStarsResponse{
ProjectStars: projectStars,
Cursor: outCursor,
}

return res, nil
}
136 changes: 136 additions & 0 deletions internal/services/gateway/api/projectstar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2024 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
"net/http"

"github.com/gorilla/mux"
"github.com/rs/zerolog"
"github.com/sorintlab/errors"

"agola.io/agola/internal/services/gateway/action"
util "agola.io/agola/internal/util"
cstypes "agola.io/agola/services/configstore/types"
gwapitypes "agola.io/agola/services/gateway/api/types"
)

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

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

func (h *CreateUserProjectStarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)

projectRef := vars["projectref"]

creq := &action.CreateUserProjectStarRequest{
ProjectRef: projectRef,
}

projectStar, err := h.ah.CreateUserProjectStar(ctx, creq)
if util.HTTPError(w, err) {
h.log.Err(err).Send()
return
}

if err := util.HTTPResponse(w, http.StatusCreated, projectStar); err != nil {
h.log.Err(err).Send()
}
}

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

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

func (h *DeleteUserProjectStarHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)

projectRef := vars["projectref"]

err := h.ah.DeleteUserProjectStar(ctx, projectRef)
if util.HTTPError(w, err) {
h.log.Err(err).Send()
return
}

if err := util.HTTPResponse(w, http.StatusNoContent, nil); err != nil {
h.log.Err(err).Send()
}
}

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

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

func (h *UserProjectStarsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
res, err := h.do(w, r)
if util.HTTPError(w, err) {
h.log.Err(err).Send()
return
}

if err := util.HTTPResponse(w, http.StatusOK, res); err != nil {
h.log.Err(err).Send()
}
}

func (h *UserProjectStarsHandler) do(w http.ResponseWriter, r *http.Request) ([]*gwapitypes.ProjectStarResponse, error) {
ctx := r.Context()

ropts, err := parseRequestOptions(r)
if err != nil {
return nil, errors.WithStack(err)
}

ares, err := h.ah.GetUserProjectStars(ctx, &action.GetUserProjectStarsRequest{Cursor: ropts.Cursor, Limit: ropts.Limit, SortDirection: action.SortDirection(ropts.SortDirection)})
if err != nil {
return nil, errors.WithStack(err)
}

projectStars := make([]*gwapitypes.ProjectStarResponse, len(ares.ProjectStars))
for i, p := range ares.ProjectStars {
projectStars[i] = createProjectStarResponse(p)
}

addCursorHeader(w, ares.Cursor)

return projectStars, nil
}

func createProjectStarResponse(o *cstypes.ProjectStar) *gwapitypes.ProjectStarResponse {
org := &gwapitypes.ProjectStarResponse{
ID: o.ID,
ProjectID: o.ProjectID,
}
return org
}
7 changes: 7 additions & 0 deletions internal/services/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ func (g *Gateway) Run(ctx context.Context) error {
userRunLogsHandler := api.NewLogsHandler(g.log, g.ah, scommon.GroupTypeUser)
userRunLogsDeleteHandler := api.NewLogsDeleteHandler(g.log, g.ah, scommon.GroupTypeUser)

createUserProjectStarHandler := api.NewCreateUserProjectStarHandler(g.log, g.ah)
deleteUserProjectStarHandler := api.NewDeleteUserProjectStarHandler(g.log, g.ah)
userProjectStarsHandler := api.NewUserProjectStarsHandler(g.log, g.ah)

userRemoteReposHandler := api.NewUserRemoteReposHandler(g.log, g.ah, g.configstoreClient)

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

apirouter.Handle("/users/{userref}/runs", authForcedHandler(userRunsHandler)).Methods("GET")
apirouter.Handle("/users/{userref}/runs/{runnumber}", authOptionalHandler(userRunHandler)).Methods("GET")
Expand Down
20 changes: 20 additions & 0 deletions services/configstore/api/types/projectstar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2024 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.

package types

type CreateProjectStarRequest struct {
UserRef string
ProjectRef string
}
39 changes: 39 additions & 0 deletions services/configstore/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,3 +778,42 @@ func (c *Client) Import(ctx context.Context, r io.Reader) (*Response, error) {
resp, err := c.GetResponse(ctx, "POST", "/import", nil, -1, common.JSONContent, r)
return resp, errors.WithStack(err)
}

func (c *Client) CreateProjectStar(ctx context.Context, req *csapitypes.CreateProjectStarRequest) (*cstypes.ProjectStar, *Response, error) {
reqj, err := json.Marshal(req)
if err != nil {
return nil, nil, errors.WithStack(err)
}

projectStar := new(cstypes.ProjectStar)
resp, err := c.GetParsedResponse(ctx, "POST", fmt.Sprintf("/users/%s/projects/%s/projectstars", req.UserRef, req.ProjectRef), nil, common.JSONContent, bytes.NewReader(reqj), projectStar)
return projectStar, resp, errors.WithStack(err)
}

func (c *Client) DeleteProjectStar(ctx context.Context, userRef string, projectRef string) (*Response, error) {
resp, err := c.GetResponse(ctx, "DELETE", fmt.Sprintf("/users/%s/projects/%s/projectstars", userRef, projectRef), nil, -1, common.JSONContent, nil)
return resp, errors.WithStack(err)
}

type GetProjectStarsOptions struct {
*ListOptions

StartProjectStarID string
}

func (o *GetProjectStarsOptions) Add(q url.Values) {
o.ListOptions.Add(q)

if o.StartProjectStarID != "" {
q.Add("startprojectstarid", o.StartProjectStarID)
}
}

func (c *Client) GetProjectStars(ctx context.Context, userRef string, opts *GetProjectStarsOptions) ([]*cstypes.ProjectStar, *Response, error) {
q := url.Values{}
opts.Add(q)

projectStars := []*cstypes.ProjectStar{}
resp, err := c.GetParsedResponse(ctx, "GET", fmt.Sprintf("/users/%s/projectstars", userRef), q, common.JSONContent, nil, &projectStars)
return projectStars, resp, errors.WithStack(err)
}
10 changes: 10 additions & 0 deletions services/gateway/api/types/projectstar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package types

type ProjectStarResponse struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
}

type CreateProjectStarRequest struct {
ProjectRef string
}
24 changes: 24 additions & 0 deletions services/gateway/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,3 +871,27 @@ func (c *Client) GetProjectCommitStatusDeliveries(ctx context.Context, projectRe
func (c *Client) ProjectCommitStatusRedelivery(ctx context.Context, projectRef string, commitStatusDeliveryID string) (*Response, error) {
return c.getResponse(ctx, "PUT", fmt.Sprintf("/projects/%s/commitstatusdeliveries/%s/redelivery", projectRef, commitStatusDeliveryID), nil, jsonContent, nil)
}

func (c *Client) GetUserProjectStars(ctx context.Context, opts *ListOptions) ([]*gwapitypes.ProjectStarResponse, *Response, error) {
q := url.Values{}
opts.Add(q)

orgs := []*gwapitypes.ProjectStarResponse{}
resp, err := c.getParsedResponse(ctx, "GET", "/user/projectstars", q, jsonContent, nil, &orgs)
return orgs, resp, errors.WithStack(err)
}

func (c *Client) CreateUserProjectStar(ctx context.Context, req *gwapitypes.CreateProjectStarRequest) (*gwapitypes.ProjectStarResponse, *Response, error) {
reqj, err := json.Marshal(req)
if err != nil {
return nil, nil, errors.WithStack(err)
}

projectStar := new(gwapitypes.ProjectStarResponse)
resp, err := c.getParsedResponse(ctx, "POST", fmt.Sprintf("/user/projects/%s/projectstars", req.ProjectRef), nil, jsonContent, bytes.NewReader(reqj), projectStar)
return projectStar, resp, errors.WithStack(err)
}

func (c *Client) DeleteUserProjectStar(ctx context.Context, projectRef string) (*Response, error) {
return c.getResponse(ctx, "DELETE", fmt.Sprintf("/user/projects/%s/projectstars", projectRef), nil, jsonContent, nil)
}
Loading

0 comments on commit f27c17b

Please sign in to comment.