package users import ( "fmt" "strings" "xcecli/common/output" "xcecli/common/request" "xcecli/config" logger "xcecli/config" userrole_filter "xcecli/filters" "xcecli/models" "github.com/spf13/cobra" "go.uber.org/zap" ) type UserRolesQuerier struct { configManager *config.ConfigManager filter *userrole_filter.UserRolesFilter fields string jsonOutput bool noHeader bool verbose bool wideFormat bool } func NewUserRolesQuerier(configManager *config.ConfigManager) *UserRolesQuerier { return &UserRolesQuerier{ configManager: configManager, filter: &userrole_filter.UserRolesFilter{}, } } func NewCurrentUserCmd(configManager *config.ConfigManager) *cobra.Command { userrolesQuerier := NewUserRolesQuerier(configManager) cmd := &cobra.Command{ Use: "whoami", Short: "Query the current loggin user", Long: `Query the current loggin user. example: xce users whoami # query the current loggin user`, RunE: func(cmd *cobra.Command, args []string) error { if err := userrolesQuerier.RunCurrentUserInfo(); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } return nil }, } return cmd } func NewListUserRolesCmd(configManager *config.ConfigManager) *cobra.Command { userrolesQuerier := NewUserRolesQuerier(configManager) cmd := &cobra.Command{ Use: "list", Short: "Query user role info", Long: `Query user role info, support filter by user. example: xce users list # query all users xce users list -l # query all users with detail xce users list -w # list users with wide format`, RunE: func(cmd *cobra.Command, args []string) error { if err := userrolesQuerier.ParseCmdArgs(cmd, args); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } if err := userrolesQuerier.RunUserRolesInfo("list"); err != nil { fmt.Println(strings.ToLower(err.Error())) return nil } return nil }, } // Add command line parameters flags := cmd.Flags() flags.StringVarP(&userrolesQuerier.filter.UserName, "user_name", "u", "", "filter by user name, e.g. -u user1,user2") flags.BoolVarP(&userrolesQuerier.wideFormat, "wided", "w", false, "output in wide format") flags.BoolVarP(&userrolesQuerier.jsonOutput, "json", "", false, "output in JSON format") flags.BoolVarP(&userrolesQuerier.noHeader, "noheader", "n", false, "output no header") return cmd } func (urq *UserRolesQuerier) ParseCmdArgs(cmd *cobra.Command, args []string) error { // No additional processing needed, all parameters are bound in flags if len(args) > 0 { urq.filter.UserName = strings.Join(args, ",") } return nil } func (urq *UserRolesQuerier) RunCurrentUserInfo() error { // Get server information serverInfo, err := urq.configManager.GetServerInfo() if err != nil { return fmt.Errorf("failed to get server info: %w", err) } // create API client apiClient := request.NewAPIClient(serverInfo.URL) // Create a response structure for current user info var currentUserResp models.UserResponse // You'll need to define this model // Make API request to get current user info using the token err = apiClient.GetRequestFromAPIServer(serverInfo.Token, "", "/xce/v1/users/current", ¤tUserResp, nil) if err != nil { return fmt.Errorf("failed to get current user info: %w", err) } // Print the username fmt.Println(currentUserResp.Data) // Adjust field name based on your model structure return nil } func (urq *UserRolesQuerier) RunUserRolesInfo(commandType string) error { // Get server information serverInfo, err := urq.configManager.GetServerInfo() if err != nil { return fmt.Errorf("failed to get server info: %w", err) } // create API client apiClient := request.NewAPIClient(serverInfo.URL) // Create query parameters params := make(map[string]string) // Get filter string filterStr, err := urq.filter.HandleUserRoleFilter() if err != nil { errMsg := request.ExtractErrorMessage(err) return fmt.Errorf("failed to get filter: [%s]", errMsg) } else if filterStr != "" { params["filter"] = filterStr } if commandType == "list" { urq.fields = "USER_NAME" params["fields"] = "user_name" } else { // Field mapping fieldsMap := models.UserRoleFieldsMap if urq.verbose && urq.fields == "" { for header := range fieldsMap { urq.fields += header + "," } urq.fields = strings.TrimSuffix(urq.fields, ",") } // Process field selection if urq.fields != "" { urq.fields = request.CleanFields(urq.fields) mappedFields, err := request.CheckAndMapFields(urq.fields, fieldsMap) if err != nil { return err } params["fields"] = strings.Join(mappedFields, ",") } } // create page printer pagePrinter := output.NewPagePrinter(output.DataTypeUsers, output.WithFields(urq.fields), output.WithFormatJson(urq.jsonOutput), output.WithNoHeader(urq.noHeader), output.WithVerbose(urq.verbose), output.WithWideFormat(urq.wideFormat)) // Record if the first page has been processed isFirstPage := true var ( resp models.UserRolesResponse loopCount int pageToken string seen map[string]struct{} // cross-page dedup state for list mode ) if commandType == "list" { seen = make(map[string]struct{}) } // Loop to get all pages of data for { if loopCount > 0 && pageToken == "" { break } resp = models.UserRolesResponse{} // reset to avoid stale rows from previous page err = apiClient.GetRequestFromAPIServer(serverInfo.Token, pageToken, "/xce/v1/users", &resp, params) if err != nil { return err } if resp.Data.Count == 0 { if isFirstPage { fmt.Println("no user found") return nil } } // For list mode, deduplicate user names client-side before printing if commandType == "list" { var unique []models.UserRoleInfo for _, row := range resp.Data.Rows { if _, ok := seen[row.UserName]; !ok { seen[row.UserName] = struct{}{} unique = append(unique, models.UserRoleInfo{UserName: row.UserName}) } } resp.Data.Rows = unique resp.Data.Count = len(unique) } // Process the first page if isFirstPage { // Start printing process pagePrinter.StartPrinting() // Prepare for printing, calculate column width and table header, etc. pagePrinter.PrepareForPrinting(&resp) isFirstPage = false } // Print the current page pagePrinter.PrintPage(&resp) // Update page token and loop counter pageToken = resp.Data.PageToken loopCount++ } // Finish printing pagePrinter.Finish() logger.Debug("user role response completed", zap.Int("pages", loopCount)) return nil } // uniqueUserNames deduplicates user names from a UserRolesResponse, preserving order. // The server returns one row per role assignment, so the same user_name appears multiple times. func uniqueUserNames(resp *models.UserRolesResponse) []string { seen := make(map[string]struct{}) var result []string for _, row := range resp.Data.Rows { if _, ok := seen[row.UserName]; !ok { seen[row.UserName] = struct{}{} result = append(result, row.UserName) } } return result }