diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 5d46746..9d145ca 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: go.mod diff --git a/activities.go b/activities.go index 5cd4dea..7317ddb 100644 --- a/activities.go +++ b/activities.go @@ -105,6 +105,19 @@ func newActivityCardJoinMember(userID UserID, username Username, memberID UserID } } +func newActivityCardUnjoinMember(userID UserID, username Username, memberID UserID, boardID BoardID, listID ListID, cardID CardID, swimlaneID SwimlaneID) Activity { + return Activity{ + UserID: userID, + Username: username, + MemberID: memberID, + BoardID: boardID, + CardID: cardID, + ListID: listID, + SwimlaneID: swimlaneID, + ActivityType: "unjoinMember", + } +} + func newActivityAddComment(userID UserID, boardID BoardID, cardID CardID, commentID CommentID, listID ListID, swimlaneID SwimlaneID) Activity { return Activity{ UserID: userID, diff --git a/activities_test.go b/activities_test.go index 4b31871..dd0d5cd 100644 --- a/activities_test.go +++ b/activities_test.go @@ -77,6 +77,25 @@ func TestActivities_newActivityCardJoinMember(t *testing.T) { ass.Equal(expected, activity) } +func TestActivities_newActivityCardUnjoinMember(t *testing.T) { + ass := assert.New(t) + expected := Activity{ + UserID: "userID", + Username: "username", + MemberID: "memberID", + BoardID: "boardID", + CardID: "cardID", + ListID: "listID", + SwimlaneID: "swimlaneID", + ActivityType: "unjoinMember", + } + activity := newActivityCardUnjoinMember( + "userID", "username", "memberID", "boardID", + "listID", "cardID", "swimlaneID", + ) + ass.Equal(expected, activity) +} + func TestActivities_newActivityAddComment(t *testing.T) { ass := assert.New(t) expected := Activity{ diff --git a/cards_integration_test.go b/cards_integration_test.go index 9122dba..37de451 100644 --- a/cards_integration_test.go +++ b/cards_integration_test.go @@ -115,7 +115,7 @@ func TestCards_SelectCardsFromMemberID(t *testing.T) { member := createTestUser(t, "Member") wekan.AddMemberToBoard(ctx, card.BoardID, BoardMember{UserID: member.ID, IsActive: true}) insertedMember, _ := wekan.GetUserFromID(ctx, member.ID) - wekan.AddMemberToCard(ctx, card.ID, member.ID) + wekan.AddMemberToCard(ctx, card, member, member) // WHEN insertedCard, _ := wekan.GetCardFromID(ctx, card.ID) @@ -164,7 +164,7 @@ func TestCards_AddCardMembership_WhenBoardIsTheSame(t *testing.T) { card := createTestCard(t, user.ID, &board.ID, &(swimlanes[0].ID), &(lists[0].ID)) // WHEN - err := wekan.AddMemberToCard(ctx, card.ID, member.ID) + err := wekan.AddMemberToCard(ctx, card, member, member) ass.Nil(err) // THEN @@ -185,7 +185,7 @@ func TestCards_AddCardMembership_WhenBoardIsNotTheSame(t *testing.T) { card := createTestCard(t, cardOwner.ID, &cardBoard.ID, &(cardSwimlanes[0].ID), &(cardLists[0].ID)) // WHEN - err := wekan.AddMemberToCard(ctx, card.ID, cardMember.ID) + err := wekan.AddMemberToCard(ctx, card, cardMember, cardMember) ass.IsType(ForbiddenOperationError{}, err) // THEN @@ -202,10 +202,10 @@ func TestCards_RemoveMemberFromCard_WhenUserIsMember(t *testing.T) { member := createTestUser(t, "Member") wekan.AddMemberToBoard(ctx, board.ID, BoardMember{UserID: member.ID, IsActive: true}) card := createTestCard(t, user.ID, &board.ID, &(swimlanes[0].ID), &(lists[0].ID)) - wekan.AddMemberToCard(ctx, card.ID, member.ID) + wekan.AddMemberToCard(ctx, card, member, member) // WHEN - err := wekan.RemoveMemberFromCard(ctx, card.ID, member.ID) + err := wekan.RemoveMemberFromCard(ctx, card, member, member) ass.Nil(err) // Then @@ -224,7 +224,7 @@ func TestCards_RemoveMemberFromCard_WhenUserIsNotMember(t *testing.T) { card := createTestCard(t, user.ID, &board.ID, &(swimlanes[0].ID), &(lists[0].ID)) // WHEN - err := wekan.RemoveMemberFromCard(ctx, card.ID, member.ID) + err := wekan.RemoveMemberFromCard(ctx, card, member, member) // Then actualCard, _ := wekan.GetCardFromID(ctx, card.ID) diff --git a/errors_integration_test.go b/errors_integration_test.go index c3648b0..e8b6bf9 100644 --- a/errors_integration_test.go +++ b/errors_integration_test.go @@ -14,7 +14,7 @@ func TestErrors_UpstreamDeadlineExceeded(t *testing.T) { badWekan := newTestBadWekan("notAWekanDB") errs := []error{ badWekan.AddMemberToBoard(ctx, "", BoardMember{}), // 0 - badWekan.AddMemberToCard(ctx, "", ""), + badWekan.AddMemberToCard(ctx, Card{}, User{}, User{}), badWekan.AddLabelToCard(ctx, "", ""), badWekan.AssertPrivileged(ctx), badWekan.CheckDocuments(ctx, UserID("")), @@ -35,7 +35,7 @@ func TestErrors_UpstreamDeadlineExceeded(t *testing.T) { badWekan.InsertTrigger(ctx, Trigger{}), badWekan.InsertUser(ctx, User{}), badWekan.InsertUsers(ctx, Users{User{}}), - badWekan.RemoveMemberFromCard(ctx, "", ""), + badWekan.RemoveMemberFromCard(ctx, Card{}, User{}, User{}), badWekan.RemoveRuleWithID(ctx, ""), ActivityID("").Check(ctx, &badWekan), BoardID("").Check(ctx, &badWekan), @@ -107,9 +107,9 @@ func TestErrors_UpstreamDeadlineExceeded(t *testing.T) { errs = append(errs, err) _, err = badWekan.SelectRulesFromBoardID(ctx, "") errs = append(errs, err) - _, err = badWekan.EnsureMemberInCard(ctx, "", "") + _, err = badWekan.EnsureMemberInCard(ctx, Card{}, User{}, User{}) errs = append(errs, err) - _, err = badWekan.EnsureMemberOutOfCard(ctx, "", "") + _, err = badWekan.EnsureMemberOutOfCard(ctx, Card{}, User{}, User{}) errs = append(errs, err) _, err = badWekan.EnsureUserIsBoardAdmin(ctx, "", "") errs = append(errs, err) diff --git a/go.mod b/go.mod index c6c4d17..8e91b74 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ory/dockertest/v3 v3.10.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.4 - go.mongodb.org/mongo-driver v1.13.0 + go.mongodb.org/mongo-driver v1.13.1 ) require ( diff --git a/go.sum b/go.sum index fac9ec1..7c50825 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.13.0 h1:67DgFFjYOCMWdtTEmKFpV3ffWlFnh+CYZ8ZS/tXWUfY= go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= +go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/init_integration_test.go b/init_integration_test.go index fc9a114..1521ebb 100644 --- a/init_integration_test.go +++ b/init_integration_test.go @@ -177,7 +177,7 @@ func TestWekan_AssertPrivileged(t *testing.T) { errs := []error{ badAdminWekan.AssertPrivileged(ctx), badAdminWekan.AddMemberToBoard(ctx, "", BoardMember{}), - badAdminWekan.AddMemberToCard(ctx, "", ""), + badAdminWekan.AddMemberToCard(ctx, Card{}, User{}, User{}), badAdminWekan.AddLabelToCard(ctx, "", ""), badAdminWekan.DisableBoardMember(ctx, "", ""), badAdminWekan.DisableUser(ctx, User{}), @@ -196,12 +196,12 @@ func TestWekan_AssertPrivileged(t *testing.T) { badAdminWekan.InsertList(ctx, List{}), badAdminWekan.InsertTrigger(ctx, Trigger{}), badAdminWekan.InsertTemplates(ctx, UserTemplates{}), - badAdminWekan.RemoveMemberFromCard(ctx, "", ""), + badAdminWekan.RemoveMemberFromCard(ctx, Card{}, User{}, User{}), badAdminWekan.RemoveRuleWithID(ctx, ""), } - _, err := badAdminWekan.EnsureMemberInCard(ctx, "", "") + _, err := badAdminWekan.EnsureMemberInCard(ctx, Card{}, User{}, User{}) errs = append(errs, err) - _, err = badAdminWekan.EnsureMemberOutOfCard(ctx, "", "") + _, err = badAdminWekan.EnsureMemberOutOfCard(ctx, Card{}, User{}, User{}) errs = append(errs, err) _, err = badAdminWekan.EnsureRuleAddTaskforceMemberExists(ctx, User{}, Board{}, BoardLabel{}) errs = append(errs, err) diff --git a/users.go b/users.go index 35b5591..956a34b 100644 --- a/users.go +++ b/users.go @@ -466,19 +466,23 @@ func (wekan *Wekan) DisableUsers(ctx context.Context, users Users) error { return nil } -func (wekan *Wekan) RemoveMemberFromCard(ctx context.Context, cardID CardID, memberID UserID) error { +func (wekan *Wekan) RemoveSelfMemberFromCard(ctx context.Context, card Card, member User) error { + return wekan.RemoveMemberFromCard(ctx, card, member, member) +} + +func (wekan *Wekan) RemoveMemberFromCard(ctx context.Context, card Card, user User, member User) error { if err := wekan.AssertPrivileged(ctx); err != nil { return err } - if err := wekan.CheckDocuments(ctx, cardID, memberID); err != nil { + if err := wekan.CheckDocuments(ctx, card.ID, member.ID); err != nil { return err } stats, err := wekan.db.Collection("cards").UpdateOne(ctx, bson.M{ - "_id": cardID, + "_id": card.ID, }, bson.M{ "$pull": bson.M{ - "members": memberID, + "members": member.ID, }, }) if stats.ModifiedCount == 0 { @@ -487,44 +491,54 @@ func (wekan *Wekan) RemoveMemberFromCard(ctx context.Context, cardID CardID, mem if err != nil { return UnexpectedMongoError{err} } + + activity := newActivityCardUnjoinMember(user.ID, member.Username, member.ID, card.BoardID, card.ListID, card.ID, card.SwimlaneID) + _, err = wekan.insertActivity(ctx, activity) + if err != nil { + return UnexpectedMongoError{err: err} + } + return nil } -func (wekan *Wekan) EnsureMemberOutOfCard(ctx context.Context, cardId CardID, memberID UserID) (bool, error) { - err := wekan.RemoveMemberFromCard(ctx, cardId, memberID) +func (wekan *Wekan) EnsureMemberOutOfCard(ctx context.Context, card Card, user User, member User) (bool, error) { + err := wekan.RemoveMemberFromCard(ctx, card, user, member) if _, ok := err.(NothingDoneError); ok { return false, nil } return err == nil, err } -func (wekan *Wekan) AddMemberToCard(ctx context.Context, cardID CardID, memberID UserID) error { +func (wekan *Wekan) AddSelfMemberToCard(ctx context.Context, card Card, member User) error { + return wekan.AddMemberToCard(ctx, card, member, member) +} + +func (wekan *Wekan) AddMemberToCard(ctx context.Context, card Card, user User, member User) error { if err := wekan.AssertPrivileged(ctx); err != nil { return err } - card, err := wekan.GetCardFromID(ctx, cardID) - if err != nil { - return err - } board, err := wekan.GetBoardFromID(ctx, card.BoardID) if err != nil { return err } - user, err := wekan.GetUserFromID(ctx, memberID) - if err != nil { - return err + + if !board.UserIsActiveMember(member) { + return ForbiddenOperationError{ + UserIsNotMemberError{member.ID}, + } } + if !board.UserIsActiveMember(user) { return ForbiddenOperationError{ - UserIsNotMemberError{memberID}, + UserIsNotMemberError{user.ID}, } } stats, err := wekan.db.Collection("cards").UpdateOne(ctx, bson.M{ - "_id": cardID, + "_id": card.ID, }, bson.M{ "$addToSet": bson.M{ - "members": memberID, + "members": member.ID, }, }) @@ -534,11 +548,18 @@ func (wekan *Wekan) AddMemberToCard(ctx context.Context, cardID CardID, memberID if stats.ModifiedCount == 0 { return NothingDoneError{} } + + activity := newActivityCardJoinMember(user.ID, member.Username, member.ID, card.BoardID, card.ListID, card.ID, card.SwimlaneID) + _, err = wekan.insertActivity(ctx, activity) + if err != nil { + return UnexpectedMongoError{err: err} + } + return nil } -func (wekan *Wekan) EnsureMemberInCard(ctx context.Context, cardID CardID, memberID UserID) (bool, error) { - err := wekan.AddMemberToCard(ctx, cardID, memberID) +func (wekan *Wekan) EnsureMemberInCard(ctx context.Context, card Card, user User, member User) (bool, error) { + err := wekan.AddMemberToCard(ctx, card, user, member) if _, ok := err.(NothingDoneError); ok { return false, nil } diff --git a/users_integration_test.go b/users_integration_test.go index 1ea3c50..6285f09 100644 --- a/users_integration_test.go +++ b/users_integration_test.go @@ -246,12 +246,16 @@ func TestUsers_EnsureMemberOutOfCard(t *testing.T) { member := createTestUser(t, "Member") card := createTestCard(t, user.ID, &board.ID, &(swimlanes[0].ID), &(lists[0].ID)) wekan.AddMemberToBoard(ctx, board.ID, BoardMember{UserID: member.ID, IsActive: true}) - wekan.AddMemberToCard(ctx, card.ID, member.ID) + wekan.AddMemberToCard(ctx, card, member, member) // WHEN - modified, err := wekan.EnsureMemberOutOfCard(ctx, card.ID, member.ID) + modified, err := wekan.EnsureMemberOutOfCard(ctx, card, member, member) + ass.Nil(err) + activities, err := wekan.SelectActivitiesFromCardID(ctx, card.ID) + ass.Nil(err) // THEN + ass.Len(activities, 3) ass.True(modified) ass.Nil(err) actualCard, err := card.ID.GetDocument(ctx, &wekan) @@ -268,9 +272,12 @@ func TestUsers_EnsureMemberOutOfCard_WhenUserIsNotBoardMember(t *testing.T) { wekan.AddMemberToBoard(ctx, board.ID, BoardMember{UserID: member.ID, IsActive: true}) // WHEN - modified, err := wekan.EnsureMemberOutOfCard(ctx, card.ID, member.ID) + modified, err := wekan.EnsureMemberOutOfCard(ctx, card, member, member) + activities, errActivities := wekan.SelectActivitiesFromCardID(ctx, card.ID) + ass.Nil(errActivities) // THEN + ass.Len(activities, 1) ass.Nil(err) ass.False(modified) actualCard, err := card.ID.GetDocument(ctx, &wekan) @@ -285,11 +292,15 @@ func TestUsers_EnsureMemberInCard_WhenUserIsActiveBoardMember(t *testing.T) { member := createTestUser(t, "Member") card := createTestCard(t, user.ID, &board.ID, &(swimlanes[0].ID), &(lists[0].ID)) wekan.AddMemberToBoard(ctx, board.ID, BoardMember{UserID: member.ID, IsActive: true}) + // WHEN - modified, err := wekan.EnsureMemberInCard(ctx, card.ID, member.ID) + modified, err := wekan.EnsureMemberInCard(ctx, card, member, member) + ass.Nil(err) + activities, err := wekan.SelectActivitiesFromCardID(ctx, card.ID) + ass.Nil(err) // THEN - ass.Nil(err) + ass.Len(activities, 2) ass.True(modified) actualCard, _ := card.ID.GetDocument(ctx, &wekan) ass.Contains(actualCard.Members, member.ID) @@ -305,9 +316,12 @@ func TestUsers_EnsureMemberInCard_WhenUserIsInactiveBoardMember(t *testing.T) { wekan.AddMemberToBoard(ctx, board.ID, BoardMember{UserID: member.ID, IsActive: false}) // WHEN - modified, err := wekan.EnsureMemberInCard(ctx, card.ID, member.ID) + modified, err := wekan.EnsureMemberInCard(ctx, card, member, member) + activities, errActivities := wekan.SelectActivitiesFromCardID(ctx, card.ID) + ass.Nil(errActivities) // THEN + ass.Len(activities, 1) ass.False(modified) ass.ErrorIs(err, UserIsNotMemberError{member.ID}) actualCard, _ := card.ID.GetDocument(ctx, &wekan)