feat: [CODE-3883]: Add controller and handler layer for favorites (#3806)
* feat: [CODE-3833]: [CODE-3844]: merged handler layer code to controller layer * feat: [CODE-3884]: Add handler layer for favorites (#3808) * format router file * Merge branch 'CODE-3883' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into CODE-3884 * feat: [CODE-3884]: moved favorite handler under user handler * merge develop * feat: [CODE-3900]: Add and populate is_favorite field in RepositoryOutput (#3821) * addressed review comments * feat: [CODE-3900]: Add and populate is_favorite field in RepositoryOutput * feat: [CODE-3884]: Add handler layer for favorites * format code * feat: [CODE-3833]: moved favorite controller files under user controller directory * removed unwanted comment * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into CODE-3883 * addressed review comments * merge develop * feat: [CODE-3883]: Add controller layer for favorites * feat: [CODE-3870]: Add only_favorites query param t
This commit is contained in:
parent
812acb6b53
commit
6315cecfbd
|
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
"github.com/harness/gitness/app/auth/authz"
|
||||
userevents "github.com/harness/gitness/app/events/user"
|
||||
"github.com/harness/gitness/app/services/refcache"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types"
|
||||
|
|
@ -37,6 +38,8 @@ type Controller struct {
|
|||
membershipStore store.MembershipStore
|
||||
publicKeyStore store.PublicKeyStore
|
||||
eventReporter *userevents.Reporter
|
||||
repoFinder refcache.RepoFinder
|
||||
favoriteStore store.FavoriteStore
|
||||
}
|
||||
|
||||
func NewController(
|
||||
|
|
@ -48,6 +51,8 @@ func NewController(
|
|||
membershipStore store.MembershipStore,
|
||||
publicKeyStore store.PublicKeyStore,
|
||||
eventReporter *userevents.Reporter,
|
||||
repoFinder refcache.RepoFinder,
|
||||
favoriteStore store.FavoriteStore,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
tx: tx,
|
||||
|
|
@ -58,6 +63,8 @@ func NewController(
|
|||
membershipStore: membershipStore,
|
||||
publicKeyStore: publicKeyStore,
|
||||
eventReporter: eventReporter,
|
||||
repoFinder: repoFinder,
|
||||
favoriteStore: favoriteStore,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
func (c *Controller) CreateFavorite(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
in *types.FavoriteResource,
|
||||
) (*types.FavoriteResource, error) {
|
||||
switch in.Type { // nolint:exhaustive
|
||||
case enum.ResourceTypeRepo:
|
||||
repo, err := c.repoFinder.FindByID(ctx, in.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't fetch repo for the user: %w", err)
|
||||
}
|
||||
if err = apiauth.CheckRepo(
|
||||
ctx,
|
||||
c.authorizer,
|
||||
session,
|
||||
repo,
|
||||
enum.PermissionRepoView); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in.ID = repo.ID
|
||||
default:
|
||||
return nil, fmt.Errorf("resource not onboarded to favorites: %s", in.Type)
|
||||
}
|
||||
|
||||
if err := c.favoriteStore.Create(ctx, session.Principal.ID, in); err != nil {
|
||||
return nil, fmt.Errorf("failed to mark %s %d as favorite: %w", in.Type, in.ID, err)
|
||||
}
|
||||
|
||||
return in, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
func (c *Controller) DeleteFavorite(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
in *types.FavoriteResource,
|
||||
) error {
|
||||
switch in.Type { // nolint:exhaustive
|
||||
case enum.ResourceTypeRepo:
|
||||
repo, err := c.repoFinder.FindByID(ctx, in.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't fetch repo for the user: %w", err)
|
||||
}
|
||||
if err = apiauth.CheckRepo(
|
||||
ctx,
|
||||
c.authorizer,
|
||||
session,
|
||||
repo,
|
||||
enum.PermissionRepoView); err != nil {
|
||||
return err
|
||||
}
|
||||
in.ID = repo.ID
|
||||
default:
|
||||
return fmt.Errorf("resource not onboarded to favorites: %s", in.Type)
|
||||
}
|
||||
|
||||
if err := c.favoriteStore.Delete(ctx, session.Principal.ID, in); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ package user
|
|||
import (
|
||||
"github.com/harness/gitness/app/auth/authz"
|
||||
userevents "github.com/harness/gitness/app/events/user"
|
||||
"github.com/harness/gitness/app/services/refcache"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
"github.com/harness/gitness/types/check"
|
||||
|
|
@ -24,7 +25,6 @@ import (
|
|||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideController,
|
||||
)
|
||||
|
|
@ -38,6 +38,8 @@ func ProvideController(
|
|||
membershipStore store.MembershipStore,
|
||||
publicKeyStore store.PublicKeyStore,
|
||||
eventReporter *userevents.Reporter,
|
||||
repoFinder refcache.RepoFinder,
|
||||
favoriteStore store.FavoriteStore,
|
||||
) *Controller {
|
||||
return NewController(
|
||||
tx,
|
||||
|
|
@ -47,5 +49,7 @@ func ProvideController(
|
|||
tokenStore,
|
||||
membershipStore,
|
||||
publicKeyStore,
|
||||
eventReporter)
|
||||
eventReporter,
|
||||
repoFinder,
|
||||
favoriteStore)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/user"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// HandleCreateFavorite returns a http.HandlerFunc that creates a new favorite.
|
||||
func HandleCreateFavorite(userCtrl *user.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
in := new(types.FavoriteResource)
|
||||
err := json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
favoriteResource, err := userCtrl.CreateFavorite(ctx, session, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, http.StatusCreated, favoriteResource)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// 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 user
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/user"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// HandleDeleteFavorite returns a http.HandlerFunc that delete a favorite.
|
||||
func HandleDeleteFavorite(userCtrl *user.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
|
||||
in := new(types.FavoriteResource)
|
||||
err := json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = userCtrl.DeleteFavorite(ctx, session, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.DeleteSuccessful(w)
|
||||
}
|
||||
}
|
||||
|
|
@ -806,6 +806,12 @@ func setupUser(r chi.Router, userCtrl *user.Controller) {
|
|||
r.Delete(fmt.Sprintf("/{%s}", request.PathParamPublicKeyIdentifier),
|
||||
handleruser.HandleDeletePublicKey(userCtrl))
|
||||
})
|
||||
|
||||
// Favorites
|
||||
r.Route("/favorite", func(r chi.Router) {
|
||||
r.Post("/", handleruser.HandleCreateFavorite(userCtrl))
|
||||
r.Delete("/", handleruser.HandleDeleteFavorite(userCtrl))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1377,13 +1377,13 @@ type (
|
|||
}
|
||||
|
||||
FavoriteStore interface {
|
||||
Create(ctx context.Context, in *types.FavoriteResource) error
|
||||
Create(ctx context.Context, principalID int64, in *types.FavoriteResource) error
|
||||
Map(
|
||||
ctx context.Context,
|
||||
principalID int64,
|
||||
resourceType enum.ResourceType,
|
||||
resourceIDs []int64,
|
||||
) (map[int64]bool, error)
|
||||
Delete(ctx context.Context, in *types.FavoriteResource) error
|
||||
Delete(ctx context.Context, principalID int64, in *types.FavoriteResource) error
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ type favorite struct {
|
|||
}
|
||||
|
||||
// Create marks the resource as favorite.
|
||||
func (s *FavoriteStore) Create(ctx context.Context, in *types.FavoriteResource) error {
|
||||
func (s *FavoriteStore) Create(ctx context.Context, principalID int64, in *types.FavoriteResource) error {
|
||||
tableName, resourceColumnName, err := getTableAndColumnName(in.Type)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "failed to fetch table and column name for favorite resource")
|
||||
|
|
@ -61,7 +61,7 @@ func (s *FavoriteStore) Create(ctx context.Context, in *types.FavoriteResource)
|
|||
|
||||
query, arg, err := db.BindNamed(favoriteResourceInsert, favorite{
|
||||
ResourceID: in.ID,
|
||||
PrincipalID: in.PrincipalID,
|
||||
PrincipalID: principalID,
|
||||
Created: time.Now().UnixMilli(),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -122,7 +122,7 @@ func (s *FavoriteStore) Map(
|
|||
}
|
||||
|
||||
// Delete unfavorites the resource.
|
||||
func (s *FavoriteStore) Delete(ctx context.Context, in *types.FavoriteResource) error {
|
||||
func (s *FavoriteStore) Delete(ctx context.Context, principalID int64, in *types.FavoriteResource) error {
|
||||
tableName, resourceColumnName, err := getTableAndColumnName(in.Type)
|
||||
if err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "failed to fetch table and column name for favorite resource")
|
||||
|
|
@ -132,7 +132,7 @@ func (s *FavoriteStore) Delete(ctx context.Context, in *types.FavoriteResource)
|
|||
|
||||
db := dbtx.GetAccessor(ctx, s.db)
|
||||
|
||||
if _, err := db.ExecContext(ctx, favoriteResourceDelete, in.ID, in.PrincipalID); err != nil {
|
||||
if _, err := db.ExecContext(ctx, favoriteResourceDelete, in.ID, principalID); err != nil {
|
||||
return database.ProcessSQLErrorf(ctx, err, "delete query failed for %s", tableName)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
controller := user.ProvideController(transactor, principalUID, authorizer, principalStore, tokenStore, membershipStore, publicKeyStore, reporter)
|
||||
favoriteStore := database.ProvideFavoriteStore(db)
|
||||
controller := user.ProvideController(transactor, principalUID, authorizer, principalStore, tokenStore, membershipStore, publicKeyStore, reporter, repoFinder, favoriteStore)
|
||||
serviceController := service.NewController(principalUID, authorizer, principalStore)
|
||||
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
|
||||
authenticator := authn.ProvideAuthenticator(config, principalStore, tokenStore)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ package types
|
|||
import "github.com/harness/gitness/types/enum"
|
||||
|
||||
type FavoriteResource struct {
|
||||
ID int64 `json:"resource_id"`
|
||||
Type enum.ResourceType `json:"resource_type"`
|
||||
PrincipalID int64
|
||||
ID int64 `json:"resource_id"`
|
||||
Type enum.ResourceType `json:"resource_type"`
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue