github.com/esnet/gdg@v0.6.1-0.20240412190737-6b6eba9c14d8/internal/service/teams.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/esnet/gdg/internal/config"
     7  	"github.com/esnet/gdg/internal/service/filters"
     8  	"log/slog"
     9  
    10  	"github.com/grafana/grafana-openapi-client-go/client/teams"
    11  	"github.com/grafana/grafana-openapi-client-go/models"
    12  	"golang.org/x/exp/maps"
    13  	"strings"
    14  
    15  	"encoding/json"
    16  	"path/filepath"
    17  
    18  	"log"
    19  )
    20  
    21  type UserPermission models.PermissionType
    22  
    23  const (
    24  	AdminUserPermission = 4
    25  )
    26  
    27  func NewTeamFilter(entries ...string) filters.Filter {
    28  	filterObj := filters.NewBaseFilter()
    29  
    30  	teamFilter := entries[0]
    31  
    32  	filterObj.AddFilter(filters.Name, teamFilter)
    33  	filterObj.AddValidation(filters.Name, func(i interface{}) bool {
    34  		switch val := i.(type) {
    35  		case string:
    36  			if filterObj.GetFilter(filters.Name) == "" {
    37  				return true
    38  			} else if val == filterObj.GetFilter(filters.Name) {
    39  				return true
    40  			}
    41  		default:
    42  			return false
    43  		}
    44  
    45  		return false
    46  	})
    47  
    48  	return filterObj
    49  }
    50  
    51  // DownloadTeams fetches all teams for a given Org
    52  func (s *DashNGoImpl) DownloadTeams(filter filters.Filter) map[*models.TeamDTO][]*models.TeamMemberDTO {
    53  	teamListing := maps.Keys(s.ListTeams(filter))
    54  	importedTeams := make(map[*models.TeamDTO][]*models.TeamMemberDTO)
    55  	teamPath := BuildResourceFolder("", config.TeamResource)
    56  	for ndx, team := range teamListing {
    57  		//Teams
    58  		teamFileName := filepath.Join(teamPath, GetSlug(team.Name), "team.json")
    59  		teamData, err := json.MarshalIndent(&teamListing[ndx], "", "\t")
    60  		if err != nil {
    61  			slog.Error("could not serialize team object for team name", "teamName", team.Name)
    62  			continue
    63  		}
    64  		//Members
    65  		memberFileName := filepath.Join(teamPath, GetSlug(team.Name), "members.json")
    66  		members, err := s.GetClient().Teams.GetTeamMembers(fmt.Sprintf("%d", team.ID))
    67  		if err != nil {
    68  			slog.Error("could not get team members object for team name", "teamName", team.Name)
    69  			continue
    70  		}
    71  		membersData, err := json.MarshalIndent(members.GetPayload(), "", "\t")
    72  		if err != nil {
    73  			slog.Error("could not serialize team members object for team name", "teamName", team.Name)
    74  			continue
    75  		}
    76  		//Writing Files
    77  		if err = s.storage.WriteFile(teamFileName, teamData); err != nil {
    78  			slog.Error("could not write file", "teamName", team.Name, "err", err)
    79  		} else if err = s.storage.WriteFile(memberFileName, membersData); err != nil {
    80  			slog.Error("could not write team members file", "teamName", team.Name, "err", err)
    81  		} else {
    82  			importedTeams[team] = members.GetPayload()
    83  		}
    84  	}
    85  	return importedTeams
    86  }
    87  
    88  // Export Teams
    89  func (s *DashNGoImpl) UploadTeams(filter filters.Filter) map[*models.TeamDTO][]*models.TeamMemberDTO {
    90  	filesInDir, err := s.storage.FindAllFiles(config.Config().GetDefaultGrafanaConfig().GetPath(config.TeamResource), true)
    91  	if err != nil {
    92  		slog.Error("failed to list files in directory for teams", "err", err)
    93  	}
    94  	exportedTeams := make(map[*models.TeamDTO][]*models.TeamMemberDTO)
    95  	//Clear previous data.
    96  	_, err = s.DeleteTeam(filter)
    97  	if err != nil {
    98  		log.Fatalf("Failed to clear previous data, aborting")
    99  	}
   100  	for _, fileLocation := range filesInDir {
   101  		if strings.HasSuffix(fileLocation, "team.json") {
   102  			//Export Team
   103  			var rawTeam []byte
   104  			if rawTeam, err = s.storage.ReadFile(fileLocation); err != nil {
   105  				slog.Error("failed to read file", "filename", fileLocation, "err", err)
   106  				continue
   107  			}
   108  			var newTeam *models.TeamDTO
   109  			if err = json.Unmarshal(rawTeam, &newTeam); err != nil {
   110  				slog.Error("failed to unmarshal file", "filename", fileLocation, "err", err)
   111  				continue
   112  			}
   113  			p := &models.CreateTeamCommand{
   114  				Name:  newTeam.Name,
   115  				Email: newTeam.Email,
   116  			}
   117  			teamCreated, err := s.GetClient().Teams.CreateTeam(p)
   118  			if err != nil {
   119  				slog.Error("failed to create team for file", "filename", fileLocation, "err", err)
   120  			}
   121  
   122  			newTeam.ID = teamCreated.GetPayload().TeamID
   123  			//Export Team Members (if exist)
   124  			var currentMembers []*models.TeamMemberDTO
   125  			var rawMembers []byte
   126  
   127  			teamMemberLocation := filepath.Join(config.Config().GetDefaultGrafanaConfig().GetPath(config.TeamResource), GetSlug(newTeam.Name), "members.json")
   128  			if rawMembers, err = s.storage.ReadFile(teamMemberLocation); err != nil {
   129  				slog.Error("failed to find team members", "filename", fileLocation, "err", err)
   130  				continue
   131  			}
   132  			var newMembers []*models.TeamMemberDTO
   133  			if err = json.Unmarshal(rawMembers, &newMembers); err != nil {
   134  				slog.Error("failed to unmarshal file", "filename", fileLocation, "err", err)
   135  				continue
   136  			}
   137  			for _, member := range newMembers {
   138  				if s.isAdminUser(member.UserID, member.Name) {
   139  					slog.Warn("skipping admin user, already added when new team is created")
   140  					continue
   141  				}
   142  				_, err := s.addTeamMember(newTeam, member)
   143  				if err != nil {
   144  					slog.Error("failed to create team member for team", "teamName", newTeam.Name, "MemberID", member.UserID, "err", err)
   145  				} else {
   146  					currentMembers = append(currentMembers, member)
   147  				}
   148  			}
   149  			exportedTeams[newTeam] = currentMembers
   150  		}
   151  	}
   152  	return exportedTeams
   153  }
   154  
   155  // List all Teams
   156  func (s *DashNGoImpl) ListTeams(filter filters.Filter) map[*models.TeamDTO][]*models.TeamMemberDTO {
   157  	result := make(map[*models.TeamDTO][]*models.TeamMemberDTO, 0)
   158  	var pageSize int64 = 99999
   159  	p := teams.NewSearchTeamsParams()
   160  	p.Perpage = &pageSize
   161  	data, err := s.GetClient().Teams.SearchTeams(p)
   162  	if err != nil {
   163  		log.Fatal("unable to list teams")
   164  	}
   165  
   166  	getTeamMembers := func(team *models.TeamDTO) {
   167  		if team.MemberCount > 0 {
   168  			result[team] = s.listTeamMembers(filter, team.ID)
   169  		} else {
   170  			result[team] = nil
   171  		}
   172  	}
   173  
   174  	for _, team := range data.GetPayload().Teams {
   175  		if filter != nil {
   176  			if filter.InvokeValidation(filters.Name, team.Name) {
   177  				getTeamMembers(team)
   178  			}
   179  		} else {
   180  			getTeamMembers(team)
   181  		}
   182  	}
   183  
   184  	return result
   185  }
   186  
   187  // Get a specific Team
   188  // Return nil if team cannot be found
   189  //func (s *DashNGoImpl) getTeam(teamName string, filter filters.Filter) *models.TeamDTO {
   190  //	teamListing := maps.Keys(s.ListTeams(filter))
   191  //	var team *models.TeamDTO
   192  //	for ndx, item := range teamListing {
   193  //		if item.Name == teamName {
   194  //			team = teamListing[ndx]
   195  //			break
   196  //		}
   197  //	}
   198  //	return team
   199  //}
   200  
   201  // DeleteTeam removes all Teams
   202  func (s *DashNGoImpl) DeleteTeam(filter filters.Filter) ([]*models.TeamDTO, error) {
   203  	teamListing := maps.Keys(s.ListTeams(filter))
   204  	var result []*models.TeamDTO
   205  	for _, team := range teamListing {
   206  		if filter != nil && !filter.ValidateAll(team.Name) {
   207  			continue
   208  		}
   209  		_, err := s.GetClient().Teams.DeleteTeamByID(fmt.Sprintf("%d", team.ID))
   210  		if err != nil {
   211  			slog.Error("failed to delete team", "teamName", team.Name)
   212  			continue
   213  		}
   214  		result = append(result, team)
   215  	}
   216  
   217  	return result, nil
   218  }
   219  
   220  // List Team Members of specific Team
   221  func (s *DashNGoImpl) listTeamMembers(filter filters.Filter, teamID int64) []*models.TeamMemberDTO {
   222  	teamIDStr := fmt.Sprintf("%d", teamID)
   223  	members, err := s.GetClient().Teams.GetTeamMembers(teamIDStr)
   224  	if err != nil {
   225  		log.Fatal(fmt.Errorf("team:  '%d' could not be found", teamID))
   226  	}
   227  
   228  	return members.GetPayload()
   229  }
   230  
   231  // Add User to a Team
   232  func (s *DashNGoImpl) addTeamMember(team *models.TeamDTO, userDTO *models.TeamMemberDTO) (string, error) {
   233  	if team == nil {
   234  		log.Fatal(fmt.Errorf("team:  '%s' could not be found", team.Name))
   235  	}
   236  	users := s.ListUsers(NewUserFilter(""))
   237  	var user *models.UserSearchHitDTO
   238  	for ndx, item := range users {
   239  		if item.Login == userDTO.Login {
   240  			user = users[ndx]
   241  			break
   242  		}
   243  	}
   244  
   245  	if user == nil {
   246  		log.Fatal(fmt.Errorf("user:  '%s' could not be found", userDTO.Login))
   247  	}
   248  	body := &models.AddTeamMemberCommand{UserID: user.ID}
   249  	msg, err := s.GetClient().Teams.AddTeamMember(fmt.Sprintf("%d", team.ID), body)
   250  	if err != nil {
   251  		slog.Info(err.Error())
   252  		errorMsg := fmt.Sprintf("failed to add member '%s' to team '%s'", userDTO.Login, team.Name)
   253  		slog.Error(errorMsg)
   254  		return "", errors.New(errorMsg)
   255  	}
   256  	if userDTO.Permission == AdminUserPermission {
   257  		adminPatch := teams.NewUpdateTeamMemberParams()
   258  		adminPatch.TeamID = fmt.Sprintf("%d", team.ID)
   259  		adminPatch.UserID = userDTO.UserID
   260  		adminPatch.Body = &models.UpdateTeamMemberCommand{Permission: AdminUserPermission}
   261  		response, err := s.GetClient().Teams.UpdateTeamMember(adminPatch)
   262  		if err != nil {
   263  			return "", err
   264  		}
   265  		slog.Debug("Updated permissions for user on team ", "username", userDTO.Name, "teamName", team.Name, "message", response.GetPayload().Message)
   266  	}
   267  
   268  	return msg.GetPayload().Message, nil
   269  }