Skip to content

Commit

Permalink
gateway: add user project favorites api
Browse files Browse the repository at this point in the history
add api to create, get, delete user project favorites.
  • Loading branch information
alessandro-sorint committed Mar 7, 2024
1 parent 409f488 commit 70b8015
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/projectfavorite.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 CreateUserProjectFavoriteRequest struct {
ProjectRef string
}

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

userID := common.CurrentUserID(ctx)

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

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

return projectFavorite, nil
}

func (h *ActionHandler) DeleteUserProjectFavorite(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.DeleteProjectFavorite(ctx, userID, projectRef); err != nil {
return util.NewAPIError(util.KindFromRemoteError(err), errors.Wrapf(err, "failed to delete project favorite"))
}
return nil
}

type GetUserProjectFavoritesRequest struct {
Cursor string

Limit int
SortDirection SortDirection
}

type GetUserProjectFavoritesResponse struct {
ProjectFavorites []*cstypes.ProjectFavorite
Cursor string
}

func (h *ActionHandler) GetUserProjectFavorites(ctx context.Context, req *GetUserProjectFavoritesRequest) (*GetUserProjectFavoritesResponse, 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
}

projectFavorites, resp, err := h.configstoreClient.GetProjectFavorites(ctx, userID, &client.GetProjectFavoritesOptions{ListOptions: &client.ListOptions{Limit: req.Limit, SortDirection: cstypes.SortDirection(sortDirection)}, StartProjectFavoriteID: inCursor.Start})
if err != nil {
return nil, util.NewAPIError(util.KindFromRemoteError(err), err)
}

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

res := &GetUserProjectFavoritesResponse{
ProjectFavorites: projectFavorites,
Cursor: outCursor,
}

return res, nil
}
136 changes: 136 additions & 0 deletions internal/services/gateway/api/projectfavorite.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 CreateUserProjectFavoriteHandler struct {
log zerolog.Logger
ah *action.ActionHandler
}

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

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

projectRef := vars["projectref"]

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

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

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

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

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

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

projectRef := vars["projectref"]

err := h.ah.DeleteUserProjectFavorite(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 UserProjectFavoritesHandler struct {
log zerolog.Logger
ah *action.ActionHandler
}

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

func (h *UserProjectFavoritesHandler) 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 *UserProjectFavoritesHandler) do(w http.ResponseWriter, r *http.Request) ([]*gwapitypes.ProjectFavoriteResponse, error) {
ctx := r.Context()

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

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

projectFavorites := make([]*gwapitypes.ProjectFavoriteResponse, len(ares.ProjectFavorites))
for i, p := range ares.ProjectFavorites {
projectFavorites[i] = createProjectFavoriteResponse(p)
}

addCursorHeader(w, ares.Cursor)

return projectFavorites, nil
}

func createProjectFavoriteResponse(o *cstypes.ProjectFavorite) *gwapitypes.ProjectFavoriteResponse {
org := &gwapitypes.ProjectFavoriteResponse{
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)

createUserProjectFavoriteHandler := api.NewCreateUserProjectFavoriteHandler(g.log, g.ah)
deleteUserProjectFavoriteHandler := api.NewDeleteUserProjectFavoriteHandler(g.log, g.ah)
userProjectFavoritesHandler := api.NewUserProjectFavoritesHandler(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}/projectfavorites", authForcedHandler(createUserProjectFavoriteHandler)).Methods("POST")
apirouter.Handle("/user/projects/{projectref}/projectfavorites", authForcedHandler(deleteUserProjectFavoriteHandler)).Methods("DELETE")
apirouter.Handle("/user/projectfavorites", authForcedHandler(userProjectFavoritesHandler)).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/projectfavorite.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 CreateProjectFavoriteRequest 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) CreateProjectFavorite(ctx context.Context, req *csapitypes.CreateProjectFavoriteRequest) (*cstypes.ProjectFavorite, *Response, error) {
reqj, err := json.Marshal(req)
if err != nil {
return nil, nil, errors.WithStack(err)
}

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

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

type GetProjectFavoritesOptions struct {
*ListOptions

StartProjectFavoriteID string
}

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

if o.StartProjectFavoriteID != "" {
q.Add("startprojectfavoriteid", o.StartProjectFavoriteID)
}
}

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

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

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

type CreateProjectFavoriteRequest 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) GetUserProjectFavorites(ctx context.Context, opts *ListOptions) ([]*gwapitypes.ProjectFavoriteResponse, *Response, error) {
q := url.Values{}
opts.Add(q)

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

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

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

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

0 comments on commit 70b8015

Please sign in to comment.