Skip to content

Commit

Permalink
Feat: Added support for multiple project owners (litmuschaos#4597)
Browse files Browse the repository at this point in the history
* Modified db schema of Owner.

Signed-off-by: aryan <[email protected]>

* Added new API GetProjectOwners.

Signed-off-by: aryan <[email protected]>

* fix: return type error.

Signed-off-by: aryan <[email protected]>

* chore(deps): Bump golang.org/x/crypto in /chaoscenter/authentication (litmuschaos#4527)

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.21.0.
- [Commits](golang/crypto@v0.18.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): Bump follow-redirects in /chaoscenter/web (litmuschaos#4529)

Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](follow-redirects/follow-redirects@v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): Bump github.com/golang/protobuf (litmuschaos#4493)

Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/golang/protobuf/releases)
- [Commits](golang/protobuf@v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/golang/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raj Das <[email protected]>

* Modified SendInvitation API.

This modification unables to send invite with the role as owner.

Signed-off-by: aryan <[email protected]>

* Modified LeaveProject API.

This modification checks if the User is the last owner of the project and if not User can leave the project.

Signed-off-by: aryan <[email protected]>

* RBAC modification `LeaveProject`.

Allows Owner to be able to leave the project.

Signed-off-by: aryan <[email protected]>

* Added `UpdateMemberRole` API.

This API is used for updating role of the member in the project.

Signed-off-by: aryan <[email protected]>

* Fixed some syntax errors.

Signed-off-by: aryan <[email protected]>

* Updated roles for owner.

Signed-off-by: aryan <[email protected]>

* Added new API `DeleteProject`.
Owner can delete project with help of this API.

Signed-off-by: aryan <[email protected]>

* Added mocks.

Signed-off-by: aryan <[email protected]>

* modified go.sum

Signed-off-by: aryan <[email protected]>

* Added condition `UpdateMemberRole`.

User cannot change role of their own, so that it will avoid edge cases like
1. User is the last owner of the project.
2. User accidentally losing owner access to the projects.

Signed-off-by: aryan <[email protected]>

* made suggested changes.

Signed-off-by: aryan <[email protected]>

* Changed DeleteProject endpoint to have url parameter.

Signed-off-by: aryan <[email protected]>

* Minor fixes.

Signed-off-by: aryan <[email protected]>

* [WIP] : Multiple project owner backend. (litmuschaos#4536)

* Modified db schema of Owner.

Signed-off-by: aryan <[email protected]>

* Added new API GetProjectOwners.

Signed-off-by: aryan <[email protected]>

* fix: return type error.

Signed-off-by: aryan <[email protected]>

* chore(deps): Bump golang.org/x/crypto in /chaoscenter/authentication (litmuschaos#4527)

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.21.0.
- [Commits](golang/crypto@v0.18.0...v0.21.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): Bump follow-redirects in /chaoscenter/web (litmuschaos#4529)

Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](follow-redirects/follow-redirects@v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): Bump github.com/golang/protobuf (litmuschaos#4493)

Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/golang/protobuf/releases)
- [Commits](golang/protobuf@v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/golang/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raj Das <[email protected]>

* Modified SendInvitation API.

This modification unables to send invite with the role as owner.

Signed-off-by: aryan <[email protected]>

* Modified LeaveProject API.

This modification checks if the User is the last owner of the project and if not User can leave the project.

Signed-off-by: aryan <[email protected]>

* RBAC modification `LeaveProject`.

Allows Owner to be able to leave the project.

Signed-off-by: aryan <[email protected]>

* Added `UpdateMemberRole` API.

This API is used for updating role of the member in the project.

Signed-off-by: aryan <[email protected]>

* Fixed some syntax errors.

Signed-off-by: aryan <[email protected]>

* Updated roles for owner.

Signed-off-by: aryan <[email protected]>

* Added new API `DeleteProject`.
Owner can delete project with help of this API.

Signed-off-by: aryan <[email protected]>

* Added mocks.

Signed-off-by: aryan <[email protected]>

* modified go.sum

Signed-off-by: aryan <[email protected]>

* Added condition `UpdateMemberRole`.

User cannot change role of their own, so that it will avoid edge cases like
1. User is the last owner of the project.
2. User accidentally losing owner access to the projects.

Signed-off-by: aryan <[email protected]>

* made suggested changes.

Signed-off-by: aryan <[email protected]>

* Changed DeleteProject endpoint to have url parameter.

Signed-off-by: aryan <[email protected]>

* Minor fixes.

Signed-off-by: aryan <[email protected]>

---------

Signed-off-by: aryan <[email protected]>
Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: Aryan Bhokare <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raj Das <[email protected]>

* Added new route .

Signed-off-by: aryan <[email protected]>

* Added `CreateProject` modal.

Added a modal CreateProject with it's controller and views.

Signed-off-by: aryan <[email protected]>

* Some changes in `CreateProjectView`.

Signed-off-by: aryan <[email protected]>

* Added `ProjectDashboardCardMenu`.

Signed-off-by: aryan <[email protected]>

* Added `ProjectDashboardCard`.

Signed-off-by: aryan <[email protected]>

* Added `DeleteProject` API mutations.

Signed-off-by: aryan <[email protected]>

* Added `ProjectDashboard`.

Signed-off-by: aryan <[email protected]>

* Added image and strings.

Signed-off-by: aryan <[email protected]>

* Modified `project entities`.

Added new fields in `Project` struct.
Added fields for filters, pagination, and some constants.
Modified `CreateProjectInput`.

Signed-off-by: aryan <[email protected]>

* [Backend] Modification in Backend for the UI.

Added Filters and pagination in Backend.
Modified API's and added a pipeline for the aggregation of results.

Signed-off-by: aryan <[email protected]>

* Added `project_util` for validation of input request.

Signed-off-by: aryan <[email protected]>

* Indent Fixes

Signed-off-by: aryan <[email protected]>

* Modification for Frontend Hook of `CreateProject` API.

Signed-off-by: aryan <[email protected]>

* Modified `ListProject` Query frontend hook.

Signed-off-by: aryan <[email protected]>

* Removed string constants and some minor changes.

Signed-off-by: aryan <[email protected]>

* Added Project Filters.

Signed-off-by: aryan <[email protected]>

* Added pagination and filter subheader in Dashboard.

Signed-off-by: aryan <[email protected]>

* modified auth-api swagger file.

Signed-off-by: aryan <[email protected]>

* Added tags section in create-project modal.

Signed-off-by: aryan <[email protected]>

* Changes due to modification of API and addition of new strings

Signed-off-by: aryan <[email protected]>

* minor changes and resolved some errors.

Signed-off-by: aryan <[email protected]>

* Added routing when clicked on the card.

Signed-off-by: aryan <[email protected]>

* Modifications in backend tests as per API updates.

Signed-off-by: aryan <[email protected]>

* Fix: NoProjects Element and NoFilteredProject Results element.

Signed-off-by: aryan <[email protected]>

* Added scroll for the project list.

Signed-off-by: aryan <[email protected]>

* Some changes in UI w.r.t Multiple Project Owner Feature.

Signed-off-by: aryan <[email protected]>

* Made search text type insensitive.

Signed-off-by: aryan <[email protected]>

* Update chaoscenter/web/src/controllers/ProjectDashboard/ProjectFilters.tsx

Co-authored-by: Hrishav <[email protected]>
Signed-off-by: Aryan Bhokare <[email protected]>

* requested changes.

Signed-off-by: aryan <[email protected]>

* removed unnecessary handle function

Signed-off-by: aryan <[email protected]>

* requested backend changes and small fixes

Signed-off-by: aryan <[email protected]>

* Changed folder structure.

Signed-off-by: aryan <[email protected]>

* requested changes.

Signed-off-by: aryan <[email protected]>

* fixed import orders

Signed-off-by: aryan <[email protected]>

* fixing RoleEditor to RoleExecuter

Signed-off-by: aryan <[email protected]>

* removed redundant deleteprojectinput

Signed-off-by: aryan <[email protected]>

* fixed bug caused in merging

Signed-off-by: aryan <[email protected]>

* fixed bug caused in merging

Signed-off-by: aryan <[email protected]>

* removed duplicate struct

Signed-off-by: aryan <[email protected]>

* Fix: frontend chaoshub test

Signed-off-by: aryan <[email protected]>

* fix: tag rendering issue in project dashboard

Signed-off-by: aryan <[email protected]>

* fix: Less user details in createProject

Signed-off-by: aryan <[email protected]>

* fix: import orders

Signed-off-by: aryan <[email protected]>

---------

Signed-off-by: aryan <[email protected]>
Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: Aryan Bhokare <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raj Das <[email protected]>
Co-authored-by: Hrishav <[email protected]>
Co-authored-by: Saranya Jena <[email protected]>
  • Loading branch information
5 people authored Jul 22, 2024
1 parent 164e280 commit 9d09211
Show file tree
Hide file tree
Showing 48 changed files with 2,442 additions and 390 deletions.
59 changes: 45 additions & 14 deletions chaoscenter/authentication/api/handlers/rest/project_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"time"

"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/types"
project_utils "github.com/litmuschaos/litmus/chaoscenter/authentication/api/utils"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
Expand Down Expand Up @@ -53,16 +55,23 @@ func GetUserWithProject(service services.ApplicationService) gin.HandlerFunc {
return
}

outputUser := user.GetUserWithProject()
request := project_utils.GetProjectFilters(c)
request.UserID = user.ID

projects, err := service.GetProjectsByUserID(outputUser.ID, false)
response, err := service.GetProjectsByUserID(request)
if err != nil {
log.Error(err)
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
return
}

outputUser.Projects = projects
outputUser := &entities.UserWithProject{
Username: user.Username,
ID: user.ID,
Email: user.Email,
Name: user.Name,
Projects: response.Projects,
}

c.JSON(http.StatusOK, gin.H{"data": outputUser})
}
Expand Down Expand Up @@ -122,9 +131,10 @@ func GetProject(service services.ApplicationService) gin.HandlerFunc {
// GetProjectsByUserID queries the project with a given userID from the database and returns it in the appropriate format
func GetProjectsByUserID(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
uID := c.MustGet("uid").(string)
projects, err := service.GetProjectsByUserID(uID, false)
if projects == nil {
request := project_utils.GetProjectFilters(c)

response, err := service.GetProjectsByUserID(request)
if response == nil || (response.TotalNumberOfProjects != nil && *response.TotalNumberOfProjects == 0) {
c.JSON(http.StatusOK, gin.H{
"message": "No projects found",
})
Expand All @@ -135,7 +145,7 @@ func GetProjectsByUserID(service services.ApplicationService) gin.HandlerFunc {
return
}

c.JSON(http.StatusOK, gin.H{"data": projects})
c.JSON(http.StatusOK, gin.H{"data": response})
}
}

Expand Down Expand Up @@ -309,6 +319,7 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
return
}
userRequest.UserID = c.MustGet("uid").(string)

// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
initialLogin, err := CheckInitialLogin(service, userRequest.UserID)
Expand All @@ -324,7 +335,17 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
return
}

userRequest.UserID = c.MustGet("uid").(string)
if userRequest.Description == nil {
// If description is not provided, set it to an empty string
emptyDescription := ""
userRequest.Description = &emptyDescription
}

if userRequest.Tags == nil {
// If tags are not provided, set it to an empty slice
emptyTags := make([]*string, 0)
userRequest.Tags = emptyTags
}

user, err := service.GetUser(userRequest.UserID)
if err != nil {
Expand All @@ -349,18 +370,22 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
// Adding user as project owner in project's member list
newMember := &entities.Member{
UserID: user.ID,
Username: user.Name,
Email: user.Email,
Role: entities.RoleOwner,
Invitation: entities.AcceptedInvitation,
JoinedAt: time.Now().UnixMilli(),
}
var members []*entities.Member
members = append(members, newMember)
state := "active"
state := string(types.MemberStateActive)
newProject := &entities.Project{
ID: pID,
Name: userRequest.ProjectName,
Members: members,
State: &state,
ID: pID,
Name: userRequest.ProjectName,
Members: members,
State: &state,
Description: userRequest.Description,
Tags: userRequest.Tags,
Audit: entities.Audit{
IsRemoved: false,
CreatedAt: time.Now().UnixMilli(),
Expand Down Expand Up @@ -436,7 +461,7 @@ func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
}

// Validating member role
if member.Role == nil || (*member.Role != entities.RoleExecutor && *member.Role != entities.RoleViewer) {
if member.Role == nil || (*member.Role != entities.RoleExecutor && *member.Role != entities.RoleViewer && *member.Role != entities.RoleOwner) {
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRole], presenter.CreateErrorResponse(utils.ErrInvalidRole))
return
}
Expand Down Expand Up @@ -734,6 +759,12 @@ func RemoveInvitation(service services.ApplicationService) gin.HandlerFunc {
return
}

uid := c.MustGet("uid").(string)
if uid == member.UserID {
c.JSON(http.StatusBadRequest, gin.H{"message": "User cannot remove invitation of themselves use Leave Project."})
return
}

invitation, err := getInvitation(service, member)
if err != nil {
log.Error(err)
Expand Down
107 changes: 95 additions & 12 deletions chaoscenter/authentication/api/handlers/rest/project_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ func TestGetUserWithProject(t *testing.T) {
Username: "testUser",
Email: "[email protected]",
}
project := &entities.Project{}
response := &entities.ListProjectResponse{}

request := &entities.ListProjectRequest{
UserID: "testUID",
}

service.On("FindUserByUsername", "testUser").Return(user, errors.New("failed"))
service.On("GetProjectsByUserID", "testUID", false).Return([]*entities.Project{project}, errors.New("failed"))
service.On("GetProjectsByUserID", request).Return(response, errors.New("failed"))

rest.GetUserWithProject(service)(c)

Expand All @@ -60,10 +64,30 @@ func TestGetUserWithProject(t *testing.T) {
Username: "testUser1",
Email: "[email protected]",
}
project := &entities.Project{}

response := &entities.ListProjectResponse{}

fieldName := entities.ProjectSortingFieldTime

request := &entities.ListProjectRequest{
UserID: "testUID",
Pagination: &entities.Pagination{
Page: 0,
Limit: 15,
},
Sort: &entities.SortInput{
Field: &fieldName,
Ascending: nil,
},
Filter: &entities.ListProjectInputFilter{
CreatedByMe: nil,
InvitedByOthers: nil,
ProjectName: nil,
},
}

service.On("FindUserByUsername", "testUser1").Return(user, nil)
service.On("GetProjectsByUserID", "testUID", false).Return([]*entities.Project{project}, nil)
service.On("GetProjectsByUserID", request).Return(response, nil)

rest.GetUserWithProject(service)(c)

Expand All @@ -87,10 +111,29 @@ func TestGetUserWithProject(t *testing.T) {
Email: "[email protected]",
Role: entities.RoleAdmin,
}
project := &entities.Project{}
response := &entities.ListProjectResponse{}

fieldName := entities.ProjectSortingFieldTime

request := &entities.ListProjectRequest{
UserID: "testUID",
Pagination: &entities.Pagination{
Page: 0,
Limit: 15,
},
Sort: &entities.SortInput{
Field: &fieldName,
Ascending: nil,
},
Filter: &entities.ListProjectInputFilter{
CreatedByMe: nil,
InvitedByOthers: nil,
ProjectName: nil,
},
}

service.On("FindUserByUsername", "testUser").Return(user, nil)
service.On("GetProjectsByUserID", "testUID", false).Return([]*entities.Project{project}, nil)
service.On("GetProjectsByUserID", request).Return(response, nil)

rest.GetUserWithProject(service)(c)

Expand All @@ -106,14 +149,30 @@ func TestGetProjectsByUserID(t *testing.T) {
w := httptest.NewRecorder()
ctx := GetTestGinContext(w)
ctx.Set("uid", "testUserID")
projects := []*entities.Project{
{
ID: "testProjectID",
Name: "Test Project",

response := &entities.ListProjectResponse{}

fieldName := entities.ProjectSortingFieldTime

request := &entities.ListProjectRequest{
UserID: "testUserID",
Pagination: &entities.Pagination{
Page: 0,
Limit: 15,
},
Sort: &entities.SortInput{
Field: &fieldName,
Ascending: nil,
},
Filter: &entities.ListProjectInputFilter{
CreatedByMe: nil,
InvitedByOthers: nil,
ProjectName: nil,
},
}

service := new(mocks.MockedApplicationService)
service.On("GetProjectsByUserID", "testUserID", false).Return(projects, errors.New("Failed"))
service.On("GetProjectsByUserID", request).Return(response, errors.New("Failed"))
rest.GetProjectsByUserID(service)(ctx)
assert.Equal(t, utils.ErrorStatusCodes[utils.ErrServerError], w.Code)
})
Expand All @@ -129,8 +188,32 @@ func TestGetProjectsByUserID(t *testing.T) {
Name: "Test Project",
},
}

response := &entities.ListProjectResponse{
Projects: projects,
}

fieldName := entities.ProjectSortingFieldTime

request := &entities.ListProjectRequest{
UserID: "testUserID",
Pagination: &entities.Pagination{
Page: 0,
Limit: 15,
},
Sort: &entities.SortInput{
Field: &fieldName,
Ascending: nil,
},
Filter: &entities.ListProjectInputFilter{
CreatedByMe: nil,
InvitedByOthers: nil,
ProjectName: nil,
},
}

service := new(mocks.MockedApplicationService)
service.On("GetProjectsByUserID", "testUserID", false).Return(projects, nil)
service.On("GetProjectsByUserID", request).Return(response, nil)
rest.GetProjectsByUserID(service)(ctx)
assert.Equal(t, http.StatusOK, w.Code)
})
Expand Down
6 changes: 3 additions & 3 deletions chaoscenter/authentication/api/mocks/rest_mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ func (m *MockedApplicationService) GetProjects(query bson.D) ([]*entities.Projec
return args.Get(0).([]*entities.Project), args.Error(1)
}

func (m *MockedApplicationService) GetProjectsByUserID(uid string, isOwner bool) ([]*entities.Project, error) {
args := m.Called(uid, isOwner)
return args.Get(0).([]*entities.Project), args.Error(1)
func (m *MockedApplicationService) GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error) {
args := m.Called(request)
return args.Get(0).(*entities.ListProjectResponse), args.Error(1)
}

func (m *MockedApplicationService) GetProjectStats() ([]*entities.ProjectStats, error) {
Expand Down
17 changes: 17 additions & 0 deletions chaoscenter/authentication/api/types/project_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package types

type MemberState string

const (
MemberStateActive MemberState = "active"
MemberStateInactive MemberState = "inactive"
)

const (
ProjectName = "projectName"
SortField = "sortField"
Ascending = "sortAscending"
CreatedByMe = "createdByMe"
Page = "page"
Limit = "limit"
)
Loading

0 comments on commit 9d09211

Please sign in to comment.