Quellcode durchsuchen

add new endpoint to update collaborator roles

Mohammed Nafees vor 3 Jahren
Ursprung
Commit
db864fb977

+ 15 - 17
api/server/handlers/project/list_collaborators.go

@@ -37,27 +37,25 @@ func (p *CollaboratorsListHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	if len(roles) > 0 {
-		userCollaboratorMap := make(map[uint]*types.Collaborator)
-
-		for _, role := range roles {
-			for _, user := range role.Users {
-				if _, ok := userCollaboratorMap[user.ID]; ok {
-					userCollaboratorMap[user.ID].RoleUIDs = append(userCollaboratorMap[user.ID].RoleUIDs, role.UniqueID)
-				} else {
-					userCollaboratorMap[user.ID] = &types.Collaborator{
-						RoleUIDs:  []string{role.UniqueID},
-						UserID:    user.ID,
-						Email:     user.Email,
-						ProjectID: proj.ID,
-					}
+	userCollaboratorMap := make(map[uint]*types.Collaborator)
+
+	for _, role := range roles {
+		for _, user := range role.Users {
+			if _, ok := userCollaboratorMap[user.ID]; ok {
+				userCollaboratorMap[user.ID].RoleUIDs = append(userCollaboratorMap[user.ID].RoleUIDs, role.UniqueID)
+			} else {
+				userCollaboratorMap[user.ID] = &types.Collaborator{
+					RoleUIDs:  []string{role.UniqueID},
+					UserID:    user.ID,
+					Email:     user.Email,
+					ProjectID: proj.ID,
 				}
 			}
 		}
+	}
 
-		for _, user := range userCollaboratorMap {
-			res = append(res, user)
-		}
+	for _, user := range userCollaboratorMap {
+		res = append(res, user)
 	}
 
 	p.WriteResult(w, r, res)

+ 3 - 4
api/server/handlers/project/list_roles.go

@@ -1,12 +1,13 @@
 package project
 
 import (
+	"fmt"
 	"net/http"
 
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
-	"github.com/porter-dev/porter/api/types"
 )
 
 type RolesListHandler struct {
@@ -23,7 +24,5 @@ func NewRolesListHandler(
 }
 
 func (p *RolesListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	var res types.ListProjectRolesResponse = []types.RoleKind{types.RoleAdmin, types.RoleDeveloper, types.RoleViewer}
-
-	p.WriteResult(w, r, res)
+	p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("deprecated"), http.StatusBadRequest))
 }

+ 102 - 0
api/server/handlers/project/update_collaborator_roles.go

@@ -0,0 +1,102 @@
+package project
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"gorm.io/gorm"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type UpdateCollaboratorRolesHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewUpdateCollaboratorRolesHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *UpdateCollaboratorRolesHandler {
+	return &UpdateCollaboratorRolesHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (p *UpdateCollaboratorRolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
+
+	userID, reqErr := requestutils.GetURLParamUint(r, "user_id")
+
+	if reqErr != nil {
+		p.HandleAPIError(w, r, reqErr)
+		return
+	}
+
+	request := &types.UpdateCollaboratorRoleRequest{}
+
+	ok := p.DecodeAndValidate(w, r, request)
+
+	if !ok {
+		return
+	}
+
+	if len(request.RoleUIDs) == 0 {
+		p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("roles cannot be empty"), http.StatusPreconditionFailed,
+		))
+		return
+	}
+
+	// check for valid user ID
+	_, err := p.Repo().User().ReadUser(userID)
+
+	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+		p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("user not found")))
+		return
+	} else if err != nil {
+		p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// check for valid project roles
+	for _, roleUID := range request.RoleUIDs {
+		role, err := p.Repo().ProjectRole().ReadProjectRole(proj.ID, roleUID)
+
+		if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+			p.HandleAPIError(w, r, apierrors.NewErrNotFound(fmt.Errorf("role not found in project: %s", roleUID)))
+			return
+		} else if err != nil {
+			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+		userIDs := role.GetUserIDs()
+		found := false
+
+		for _, id := range userIDs {
+			if id == userID {
+				found = true
+				break
+			}
+		}
+
+		if !found {
+			userIDs = append(userIDs, userID)
+
+			err := p.Repo().ProjectRole().UpdateUsersInProjectRole(proj.ID, roleUID, userIDs)
+
+			if err != nil {
+				p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+				return
+			}
+		}
+	}
+}

+ 28 - 0
api/server/router/project.go

@@ -386,6 +386,34 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
+	// PATCH /api/projects/{project_id}/collaborators/{user_id} -> project.UpdateCollaboratorWithRolesHandler
+	updateCollaboratorRolesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbUpdate,
+			Method: types.HTTPVerbPatch,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/collaborators/{user_id}",
+			},
+			Scopes: []types.PermissionScope{
+				types.SettingsScope,
+				types.ProjectScope,
+			},
+		},
+	)
+
+	updateCollaboratorRolesHandler := project.NewUpdateCollaboratorRolesHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: updateCollaboratorRolesEndpoint,
+		Handler:  updateCollaboratorRolesHandler,
+		Router:   r,
+	})
+
 	// GET /api/projects/{project_id}/roles -> project.NewRolesListHandler
 	listRolesEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 2 - 19
api/types/project.go

@@ -45,11 +45,7 @@ type ListProjectInfraResponse []*Infra
 
 type GetProjectPolicyResponse []*PolicyDocument
 
-type ListProjectRolesResponse []RoleKind
-
 type Collaborator struct {
-	ID        uint     `json:"id,omitempty"`
-	Kind      string   `json:"kind,omitempty"`
 	RoleUIDs  []string `json:"roles"`
 	UserID    uint     `json:"user_id"`
 	Email     string   `json:"email"`
@@ -58,21 +54,8 @@ type Collaborator struct {
 
 type ListCollaboratorsResponse []*Collaborator
 
-type UpdateRoleRequest struct {
-	UserID uint   `json:"user_id,required"`
-	Kind   string `json:"kind,required"`
-}
-
-type UpdateRoleResponse struct {
-	*Role
-}
-
-type DeleteRoleRequest struct {
-	UserID uint `schema:"user_id,required"`
-}
-
-type DeleteRoleResponse struct {
-	*Role
+type UpdateCollaboratorRoleRequest struct {
+	RoleUIDs []string `json:"roles" form:"required"`
 }
 
 type GetBillingTokenResponse struct {

+ 10 - 0
internal/models/project_role.go

@@ -39,3 +39,13 @@ func (role *ProjectRole) IsDefaultRole() bool {
 		role.UniqueID == fmt.Sprintf("%d-%s", role.ProjectID, types.RoleDeveloper) ||
 		role.UniqueID == fmt.Sprintf("%d-%s", role.ProjectID, types.RoleViewer)
 }
+
+func (role *ProjectRole) GetUserIDs() []uint {
+	var ids []uint
+
+	for _, user := range role.Users {
+		ids = append(ids, user.ID)
+	}
+
+	return ids
+}