From e6c1f0638400c12f44f8e552878779b7a304c014 Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Tue, 15 Oct 2024 23:47:07 -0400 Subject: [PATCH 1/6] scaffolding --- examples/gno.land/p/sys/teams/types.gno | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/gno.land/p/sys/teams/types.gno diff --git a/examples/gno.land/p/sys/teams/types.gno b/examples/gno.land/p/sys/teams/types.gno new file mode 100644 index 00000000000..e69de29bb2d From 91ff1cd8c1cd03a5738a69952528ea885ca4f37d Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Wed, 16 Oct 2024 00:22:53 -0400 Subject: [PATCH 2/6] change folder --- examples/gno.land/{p => r}/sys/teams/types.gno | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/gno.land/{p => r}/sys/teams/types.gno (100%) diff --git a/examples/gno.land/p/sys/teams/types.gno b/examples/gno.land/r/sys/teams/types.gno similarity index 100% rename from examples/gno.land/p/sys/teams/types.gno rename to examples/gno.land/r/sys/teams/types.gno From 4fe30a9fbe38f65439ce382e61f47609dbb25ea1 Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Wed, 16 Oct 2024 00:37:11 -0400 Subject: [PATCH 3/6] types --- examples/gno.land/r/sys/teams/types.gno | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/gno.land/r/sys/teams/types.gno b/examples/gno.land/r/sys/teams/types.gno index e69de29bb2d..c2e634f74b1 100644 --- a/examples/gno.land/r/sys/teams/types.gno +++ b/examples/gno.land/r/sys/teams/types.gno @@ -0,0 +1,22 @@ +package teams + +import ( + "errors" + "std" + "strings" + + "gno.land/p/demo/avl" + "gno.land/p/demo/users" +) + +type Team struct { + Name string + Owners []std.Address + Members []TeamMember + Permissions []string +} + +type TeamMember struct { + User users.User, + Permissions []string +} \ No newline at end of file From 19d8e1d5d9c3f28ef2586c0c49dd715b3ea246ff Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Thu, 17 Oct 2024 00:16:49 -0400 Subject: [PATCH 4/6] basic functionality, tests --- examples/gno.land/r/sys/teams/teams.gno | 118 +++++++++++++++++++ examples/gno.land/r/sys/teams/teams_test.gno | 101 ++++++++++++++++ examples/gno.land/r/sys/teams/types.gno | 18 ++- 3 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 examples/gno.land/r/sys/teams/teams.gno create mode 100644 examples/gno.land/r/sys/teams/teams_test.gno diff --git a/examples/gno.land/r/sys/teams/teams.gno b/examples/gno.land/r/sys/teams/teams.gno new file mode 100644 index 00000000000..2c8064a0ef1 --- /dev/null +++ b/examples/gno.land/r/sys/teams/teams.gno @@ -0,0 +1,118 @@ + +package teams + +import ( + "errors" + "std" + "strings" + + "gno.land/p/demo/avl" + "gno.land/p/demo/users" +) + +var Teams *avl.Tree + +func init() { + Teams = avl.NewTree() +} + +func CreateTeam(teamAddress std.Address, teamName string, owner std.Address) error { + if _, exists := Teams.Get(teamAddress.String()); exists { + return errors.New("team already exists") + } + + team := &Team{ + Name: teamName, + Owners: []std.Address{owner}, + } + + Teams.Set(teamAddress.String(), team) + return nil +} + +func AddMember(teamAddress std.Address, user users.User) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + // Check if the user is already a member + for _, member := range team.Members { + if member.User.Address == user.Address { + return errors.New("user is already a member of the team") + } + } + + team.Members = append(team.Members, TeamMember{User: user}) + Teams.Set(teamAddress.String(), team) + return nil +} + +func RemoveMember(teamAddress std.Address, userAddress std.Address) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + for i, member := range team.Members { + if member.User.Address == userAddress { + team.Members = append(team.Members[:i], team.Members[i+1:]...) + Teams.Set(teamAddress.String(), team) + return nil + } + } + + return errors.New("member not found") +} + +func AddPermission(teamAddress std.Address, permission string) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + for _, perm := range team.Permissions { + if strings.EqualFold(perm, permission) { + return errors.New("permission already exists in the team") + } + } + + team.Permissions = append(team.Permissions, permission) + Teams.Set(teamAddress.String(), team) + return nil +} + +func RemovePermission(teamAddress std.Address, permission string) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + for i, perm := range team.Permissions { + if strings.EqualFold(perm, permission) { + team.Permissions = append(team.Permissions[:i], team.Permissions[i+1:]...) + break + } + } + + for i, member := range team.Members { + for j, memberPerm := range member.Permissions { + if strings.EqualFold(memberPerm, permission) { + team.Members[i].Permissions = append(member.Permissions[:j], member.Permissions[j+1:]...) + break + } + } + } + + Teams.Set(teamAddress.String(), team) + return nil +} + +// helper function to retrieve a team by address +func getTeam(teamAddress std.Address) (*Team, error) { + team, exists := Teams.Get(teamAddress.String()) + if !exists { + return nil, errors.New("team not found") + } + return team.(*Team), nil +} diff --git a/examples/gno.land/r/sys/teams/teams_test.gno b/examples/gno.land/r/sys/teams/teams_test.gno new file mode 100644 index 00000000000..74b208f6b76 --- /dev/null +++ b/examples/gno.land/r/sys/teams/teams_test.gno @@ -0,0 +1,101 @@ +package teams + +import ( + "testing" + "std" + "gno.land/p/demo/users" + "gno.land/r/sys/teams" +) + +func TestCreateTeam(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + teamName := "Test Team" + owner := std.Address("g1owneraddress") + + err := teams.CreateTeam(teamAddress, teamName, owner) + if err != nil { + t.Errorf("Failed to create team: %v", err) + } + + // Attempt to create a team with the same address, should fail + err = teams.CreateTeam(teamAddress, teamName, owner) + if err == nil { + t.Errorf("Expected error when creating a duplicate team, got nil") + } +} + +func TestAddMember(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + user := users.User{Name: "Test User", Address: std.Address("g1useraddress")} + err := teams.AddMember(teamAddress, user) + if err != nil { + t.Errorf("Failed to add member: %v", err) + } + + // Attempt to add the same member again, should fail + err = teams.AddMember(teamAddress, user) + if err == nil { + t.Errorf("Expected error when adding a duplicate member, got nil") + } +} + +func TestRemoveMember(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + user := users.User{Name: "Test User", Address: std.Address("g1useraddress")} + _ = teams.AddMember(teamAddress, user) + + err := teams.RemoveMember(teamAddress, user.Address) + if err != nil { + t.Errorf("Failed to remove member: %v", err) + } + + // Attempt to remove a non-existing member, should fail + err = teams.RemoveMember(teamAddress, user.Address) + if err == nil { + t.Errorf("Expected error when removing a non-existing member, got nil") + } +} + +func TestAddPermission(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + permission := "manage_tasks" + err := teams.AddPermission(teamAddress, permission) + if err != nil { + t.Errorf("Failed to add permission: %v", err) + } + + // Attempt to add the same permission again, should fail + err = teams.AddPermission(teamAddress, permission) + if err == nil { + t.Errorf("Expected error when adding a duplicate permission, got nil") + } +} + +func TestRemovePermission(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + permission := "manage_tasks" + _ = teams.AddPermission(teamAddress, permission) + + err := teams.RemovePermission(teamAddress, permission) + if err != nil { + t.Errorf("Failed to remove permission: %v", err) + } + + // Attempt to remove a non-existing permission, should not fail + err = teams.RemovePermission(teamAddress, permission) + if err != nil { + t.Errorf("Expected no error when removing a non-existing permission, got %v", err) + } +} diff --git a/examples/gno.land/r/sys/teams/types.gno b/examples/gno.land/r/sys/teams/types.gno index c2e634f74b1..016699dbb45 100644 --- a/examples/gno.land/r/sys/teams/types.gno +++ b/examples/gno.land/r/sys/teams/types.gno @@ -1,22 +1,20 @@ package teams import ( - "errors" "std" "strings" - "gno.land/p/demo/avl" "gno.land/p/demo/users" ) type Team struct { - Name string - Owners []std.Address - Members []TeamMember - Permissions []string + Name string + Owners []std.Address + Members []TeamMember + Permissions []string } -type TeamMember struct { - User users.User, - Permissions []string -} \ No newline at end of file +type TeamMember struct { + User users.User + Permissions []string +} From c0052da60e04936c360f02d531d69b31d3ae56bc Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Thu, 17 Oct 2024 05:31:39 -0400 Subject: [PATCH 5/6] member permissions --- examples/gno.land/r/sys/teams/teams.gno | 61 ++++++++++++++++++++ examples/gno.land/r/sys/teams/teams_test.gno | 51 +++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/r/sys/teams/teams.gno b/examples/gno.land/r/sys/teams/teams.gno index 2c8064a0ef1..1bbbbe5e087 100644 --- a/examples/gno.land/r/sys/teams/teams.gno +++ b/examples/gno.land/r/sys/teams/teams.gno @@ -108,6 +108,67 @@ func RemovePermission(teamAddress std.Address, permission string) error { return nil } +// AddMemberPermission adds a permission to a specific member of the team +func AddMemberPermission(teamAddress std.Address, userAddress std.Address, permission string) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + // Check if the permission exists in the team's permissions + permissionExists := false + for _, perm := range team.Permissions { + if strings.EqualFold(perm, permission) { + permissionExists = true + break + } + } + if !permissionExists { + return errors.New("permission does not exist in the team") + } + + // Find the member and add the permission if they don't already have it + for i, member := range team.Members { + if member.User.Address == userAddress { + for _, memberPerm := range member.Permissions { + if strings.EqualFold(memberPerm, permission) { + return errors.New("member already has the permission") + } + } + team.Members[i].Permissions = append(team.Members[i].Permissions, permission) + Teams.Set(teamAddress.String(), team) + return nil + } + } + + return errors.New("member not found") +} + +// RemoveMemberPermission removes a permission from a specific member of the team +func RemoveMemberPermission(teamAddress std.Address, userAddress std.Address, permission string) error { + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + // Find the member and remove the permission + for i, member := range team.Members { + if member.User.Address == userAddress { + for j, memberPerm := range member.Permissions { + if strings.EqualFold(memberPerm, permission) { + team.Members[i].Permissions = append(member.Permissions[:j], member.Permissions[j+1:]...) + Teams.Set(teamAddress.String(), team) + return nil + } + } + return errors.New("permission not found for the member") + } + } + + return errors.New("member not found") +} + + // helper function to retrieve a team by address func getTeam(teamAddress std.Address) (*Team, error) { team, exists := Teams.Get(teamAddress.String()) diff --git a/examples/gno.land/r/sys/teams/teams_test.gno b/examples/gno.land/r/sys/teams/teams_test.gno index 74b208f6b76..c544b30d74e 100644 --- a/examples/gno.land/r/sys/teams/teams_test.gno +++ b/examples/gno.land/r/sys/teams/teams_test.gno @@ -1,10 +1,10 @@ -package teams +package teams_test import ( "testing" "std" "gno.land/p/demo/users" - "gno.land/r/sys/teams" + "gno.land/p/demo/teams" ) func TestCreateTeam(t *testing.T) { @@ -99,3 +99,50 @@ func TestRemovePermission(t *testing.T) { t.Errorf("Expected no error when removing a non-existing permission, got %v", err) } } + +func TestAddMemberPermission(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + user := users.User{Name: "Test User", Address: std.Address("g1useraddress")} + _ = teams.AddMember(teamAddress, user) + + permission := "manage_tasks" + _ = teams.AddPermission(teamAddress, permission) + + err := teams.AddMemberPermission(teamAddress, user.Address, permission) + if err != nil { + t.Errorf("Failed to add permission to member: %v", err) + } + + // Attempt to add a permission that does not exist in the team, should fail + err = teams.AddMemberPermission(teamAddress, user.Address, "non_existing_permission") + if err == nil { + t.Errorf("Expected error when adding a non-existing permission to member, got nil") + } +} + +func TestRemoveMemberPermission(t *testing.T) { + teamAddress := std.Address("g1teamaddress") + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, "Test Team", owner) + + user := users.User{Name: "Test User", Address: std.Address("g1useraddress")} + _ = teams.AddMember(teamAddress, user) + + permission := "manage_tasks" + _ = teams.AddPermission(teamAddress, permission) + _ = teams.AddMemberPermission(teamAddress, user.Address, permission) + + err := teams.RemoveMemberPermission(teamAddress, user.Address, permission) + if err != nil { + t.Errorf("Failed to remove permission from member: %v", err) + } + + // Attempt to remove a permission that the member does not have, should not fail + err = teams.RemoveMemberPermission(teamAddress, user.Address, permission) + if err != nil { + t.Errorf("Expected no error when removing a non-existing permission from member, got %v", err) + } +} From 41ce1284d88a9426a4253bd5f5cbe50749792b60 Mon Sep 17 00:00:00 2001 From: irreverentsimplicity Date: Fri, 18 Oct 2024 23:50:20 -0400 Subject: [PATCH 6/6] unique team names --- examples/gno.land/r/sys/teams/teams.gno | 122 +++++++++++-------- examples/gno.land/r/sys/teams/teams_test.gno | 43 +++++-- 2 files changed, 101 insertions(+), 64 deletions(-) diff --git a/examples/gno.land/r/sys/teams/teams.gno b/examples/gno.land/r/sys/teams/teams.gno index 1bbbbe5e087..d7a3fec80b3 100644 --- a/examples/gno.land/r/sys/teams/teams.gno +++ b/examples/gno.land/r/sys/teams/teams.gno @@ -1,4 +1,3 @@ - package teams import ( @@ -11,9 +10,11 @@ import ( ) var Teams *avl.Tree +var TeamNames *avl.Tree func init() { Teams = avl.NewTree() + TeamNames = avl.NewTree() } func CreateTeam(teamAddress std.Address, teamName string, owner std.Address) error { @@ -21,12 +22,17 @@ func CreateTeam(teamAddress std.Address, teamName string, owner std.Address) err return errors.New("team already exists") } + if _, nameExists := TeamNames.Get(strings.ToLower(teamName)); nameExists { + return errors.New("team name already exists") + } + team := &Team{ Name: teamName, Owners: []std.Address{owner}, } Teams.Set(teamAddress.String(), team) + TeamNames.Set(strings.ToLower(teamName), teamAddress) return nil } @@ -110,64 +116,63 @@ func RemovePermission(teamAddress std.Address, permission string) error { // AddMemberPermission adds a permission to a specific member of the team func AddMemberPermission(teamAddress std.Address, userAddress std.Address, permission string) error { - team, err := getTeam(teamAddress) - if err != nil { - return err - } - - // Check if the permission exists in the team's permissions - permissionExists := false - for _, perm := range team.Permissions { - if strings.EqualFold(perm, permission) { - permissionExists = true - break - } - } - if !permissionExists { - return errors.New("permission does not exist in the team") - } - - // Find the member and add the permission if they don't already have it - for i, member := range team.Members { - if member.User.Address == userAddress { - for _, memberPerm := range member.Permissions { - if strings.EqualFold(memberPerm, permission) { - return errors.New("member already has the permission") - } - } - team.Members[i].Permissions = append(team.Members[i].Permissions, permission) - Teams.Set(teamAddress.String(), team) - return nil - } - } - - return errors.New("member not found") + team, err := getTeam(teamAddress) + if err != nil { + return err + } + + // Check if the permission exists in the team's permissions + permissionExists := false + for _, perm := range team.Permissions { + if strings.EqualFold(perm, permission) { + permissionExists = true + break + } + } + if !permissionExists { + return errors.New("permission does not exist in the team") + } + + // Find the member and add the permission if they don't already have it + for i, member := range team.Members { + if member.User.Address == userAddress { + for _, memberPerm := range member.Permissions { + if strings.EqualFold(memberPerm, permission) { + return errors.New("member already has the permission") + } + } + team.Members[i].Permissions = append(team.Members[i].Permissions, permission) + Teams.Set(teamAddress.String(), team) + return nil + } + } + + return errors.New("member not found") } // RemoveMemberPermission removes a permission from a specific member of the team func RemoveMemberPermission(teamAddress std.Address, userAddress std.Address, permission string) error { - team, err := getTeam(teamAddress) - if err != nil { - return err - } - - // Find the member and remove the permission - for i, member := range team.Members { - if member.User.Address == userAddress { - for j, memberPerm := range member.Permissions { - if strings.EqualFold(memberPerm, permission) { - team.Members[i].Permissions = append(member.Permissions[:j], member.Permissions[j+1:]...) - Teams.Set(teamAddress.String(), team) - return nil - } - } - return errors.New("permission not found for the member") - } - } - - return errors.New("member not found") -} + team, err := getTeam(teamAddress) + if err != nil { + return err + } + // Find the member and remove the permission + for i, member := range team.Members { + if member.User.Address == userAddress { + for j, memberPerm := range member.Permissions { + if strings.EqualFold(memberPerm, permission) { + team.Members[i].Permissions = append(member.Permissions[:j], member.Permissions[j+1:]...) + Teams.Set(teamAddress.String(), team) + return nil + } + } + return errors.New("permission not found for the member") + } + } + + return errors.New("member not found") +} // helper function to retrieve a team by address func getTeam(teamAddress std.Address) (*Team, error) { @@ -177,3 +182,12 @@ func getTeam(teamAddress std.Address) (*Team, error) { } return team.(*Team), nil } + +// GetTeamByName retrieves a team by its name +func GetTeamByName(teamName string) (*Team, error) { + teamAddress, exists := TeamNames.Get(strings.ToLower(teamName)) + if !exists { + return nil, errors.New("team not found") + } + return getTeam(teamAddress.(std.Address)) +} diff --git a/examples/gno.land/r/sys/teams/teams_test.gno b/examples/gno.land/r/sys/teams/teams_test.gno index c544b30d74e..054ea04b017 100644 --- a/examples/gno.land/r/sys/teams/teams_test.gno +++ b/examples/gno.land/r/sys/teams/teams_test.gno @@ -1,10 +1,10 @@ -package teams_test +package teams import ( "testing" "std" "gno.land/p/demo/users" - "gno.land/p/demo/teams" + "gno.land/r/sys/teams" ) func TestCreateTeam(t *testing.T) { @@ -22,6 +22,13 @@ func TestCreateTeam(t *testing.T) { if err == nil { t.Errorf("Expected error when creating a duplicate team, got nil") } + + // Attempt to create a team with the same name, should fail + newTeamAddress := std.Address("g1newteamaddress") + err = teams.CreateTeam(newTeamAddress, teamName, owner) + if err == nil { + t.Errorf("Expected error when creating a team with duplicate name, got nil") + } } func TestAddMember(t *testing.T) { @@ -126,23 +133,39 @@ func TestAddMemberPermission(t *testing.T) { func TestRemoveMemberPermission(t *testing.T) { teamAddress := std.Address("g1teamaddress") owner := std.Address("g1owneraddress") - _ = teams.CreateTeam(teamAddress, "Test Team", owner) - - user := users.User{Name: "Test User", Address: std.Address("g1useraddress")} + _ = teams.CreateTeam(teamAddress, "Test Team2", owner) + + user := users.User{Name: "Test User2", Address: std.Address("g1useraddress2")} _ = teams.AddMember(teamAddress, user) - + permission := "manage_tasks" _ = teams.AddPermission(teamAddress, permission) _ = teams.AddMemberPermission(teamAddress, user.Address, permission) - + err := teams.RemoveMemberPermission(teamAddress, user.Address, permission) if err != nil { t.Errorf("Failed to remove permission from member: %v", err) } +} + +func TestGetTeamByName(t *testing.T) { + teamAddress := std.Address("g1teamaddressunique") + teamName := "Unique Team" + owner := std.Address("g1owneraddress") + _ = teams.CreateTeam(teamAddress, teamName, owner) - // Attempt to remove a permission that the member does not have, should not fail - err = teams.RemoveMemberPermission(teamAddress, user.Address, permission) + team, err := teams.GetTeamByName(teamName) if err != nil { - t.Errorf("Expected no error when removing a non-existing permission from member, got %v", err) + t.Errorf("Failed to get team by name: %v", err) + } + + if team.Name != teamName { + t.Errorf("Expected team name to be %v, got %v", teamName, team.Name) + } + + // Attempt to get a non-existing team by name, should fail + _, err = teams.GetTeamByName("NonExistingTeam") + if err == nil { + t.Errorf("Expected error when getting a non-existing team by name, got nil") } }