package cmd import ( "context" "encoding/json" "fmt" "os" "strings" "gogs.dmsc.dev/arp/arp_cli/client" "gogs.dmsc.dev/arp/arp_cli/config" "github.com/AlecAivazis/survey/v2" "github.com/olekukonko/tablewriter" "github.com/urfave/cli/v3" ) // RoleCommand returns the role command func RoleCommand() *cli.Command { return &cli.Command{ Name: "role", Usage: "Manage roles", Description: `Manage ARP roles. Roles group permissions for access control. Use this command to create, list, update, and delete roles.`, Commands: []*cli.Command{ { Name: "list", Aliases: []string{"ls"}, Usage: "List all roles", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "json", Aliases: []string{"j"}, Usage: "Output as JSON", }, }, Action: roleList, }, { Name: "get", Usage: "Get a role by ID", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Role ID", Required: true, }, &cli.BoolFlag{ Name: "json", Aliases: []string{"j"}, Usage: "Output as JSON", }, }, Action: roleGet, }, { Name: "create", Usage: "Create a new role", Action: roleCreate, Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Usage: "Role name", }, &cli.StringFlag{ Name: "description", Aliases: []string{"d"}, Usage: "Role description", }, &cli.StringSliceFlag{ Name: "permissions", Aliases: []string{"p"}, Usage: "Permission IDs", }, }, }, { Name: "update", Usage: "Update a role", Action: roleUpdate, Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Role ID", Required: true, }, &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Usage: "Role name", }, &cli.StringFlag{ Name: "description", Aliases: []string{"d"}, Usage: "Role description", }, &cli.StringSliceFlag{ Name: "permissions", Aliases: []string{"p"}, Usage: "Permission IDs", }, }, }, { Name: "delete", Usage: "Delete a role", Action: roleDelete, Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Role ID", Required: true, }, &cli.BoolFlag{ Name: "yes", Aliases: []string{"y"}, Usage: "Skip confirmation", }, }, }, }, } } type RoleDetail struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Permissions []Permission `json:"permissions"` } type Permission struct { ID string `json:"id"` Code string `json:"code"` Description string `json:"description"` } func roleList(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) query := "query Roles { roles { id name description permissions { id code } } }" resp, err := c.Query(query, nil) if err != nil { return err } var result struct { Roles []RoleDetail `json:"roles"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if cmd.Bool("json") { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(result.Roles) } if len(result.Roles) == 0 { fmt.Println("No roles found.") return nil } table := tablewriter.NewWriter(os.Stdout) table.Header([]string{"ID", "Name", "Description", "Permissions"}) for _, r := range result.Roles { perms := make([]string, len(r.Permissions)) for i, p := range r.Permissions { perms[i] = p.Code } table.Append([]string{r.ID, r.Name, r.Description, strings.Join(perms, ", ")}) } table.Render() return nil } func roleGet(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) id := cmd.String("id") query := "query Role($id: ID!) { role(id: $id) { id name description permissions { id code description } } }" resp, err := c.Query(query, map[string]interface{}{"id": id}) if err != nil { return err } var result struct { Role *RoleDetail `json:"role"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.Role == nil { return fmt.Errorf("role not found") } if cmd.Bool("json") { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(result.Role) } r := result.Role fmt.Printf("ID: %s\n", r.ID) fmt.Printf("Name: %s\n", r.Name) fmt.Printf("Description: %s\n", r.Description) fmt.Printf("Permissions:\n") for _, p := range r.Permissions { fmt.Printf(" - %s (%s): %s\n", p.Code, p.ID, p.Description) } return nil } func roleCreate(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } name := cmd.String("name") description := cmd.String("description") permissions := cmd.StringSlice("permissions") if name == "" { prompt := &survey.Input{Message: "Role name:"} if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil { return err } } if description == "" { prompt := &survey.Input{Message: "Description:"} if err := survey.AskOne(prompt, &description, survey.WithValidator(survey.Required)); err != nil { return err } } if len(permissions) == 0 { var permissionsStr string prompt := &survey.Input{Message: "Permission IDs (comma-separated):"} if err := survey.AskOne(prompt, &permissionsStr, survey.WithValidator(survey.Required)); err != nil { return err } for _, p := range strings.Split(permissionsStr, ",") { permissions = append(permissions, strings.TrimSpace(p)) } } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) mutation := `mutation CreateRole($input: NewRole!) { createRole(input: $input) { id name description permissions { id code } } }` input := map[string]interface{}{ "name": name, "description": description, "permissions": permissions, } resp, err := c.Mutation(mutation, map[string]interface{}{"input": input}) if err != nil { return err } var result struct { CreateRole *RoleDetail `json:"createRole"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.CreateRole == nil { return fmt.Errorf("failed to create role") } fmt.Printf("Role created successfully!\n") fmt.Printf("ID: %s\n", result.CreateRole.ID) fmt.Printf("Name: %s\n", result.CreateRole.Name) return nil } func roleUpdate(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } id := cmd.String("id") name := cmd.String("name") description := cmd.String("description") permissions := cmd.StringSlice("permissions") if name == "" && description == "" && len(permissions) == 0 { fmt.Println("No updates provided. Use flags to specify what to update.") return nil } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) input := make(map[string]interface{}) if name != "" { input["name"] = name } if description != "" { input["description"] = description } if len(permissions) > 0 { input["permissions"] = permissions } mutation := `mutation UpdateRole($id: ID!, $input: UpdateRoleInput!) { updateRole(id: $id, input: $input) { id name description permissions { id code } } }` resp, err := c.Mutation(mutation, map[string]interface{}{"id": id, "input": input}) if err != nil { return err } var result struct { UpdateRole *RoleDetail `json:"updateRole"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.UpdateRole == nil { return fmt.Errorf("role not found") } fmt.Printf("Role updated successfully!\n") fmt.Printf("ID: %s\n", result.UpdateRole.ID) fmt.Printf("Name: %s\n", result.UpdateRole.Name) return nil } func roleDelete(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } id := cmd.String("id") skipConfirm := cmd.Bool("yes") if !skipConfirm { confirm := false prompt := &survey.Confirm{ Message: fmt.Sprintf("Are you sure you want to delete role %s?", id), Default: false, } if err := survey.AskOne(prompt, &confirm); err != nil { return err } if !confirm { fmt.Println("Deletion cancelled.") return nil } } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) mutation := `mutation DeleteRole($id: ID!) { deleteRole(id: $id) }` resp, err := c.Mutation(mutation, map[string]interface{}{"id": id}) if err != nil { return err } var result struct { DeleteRole bool `json:"deleteRole"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.DeleteRole { fmt.Printf("Role %s deleted successfully.\n", id) } else { fmt.Printf("Failed to delete role %s.\n", id) } return nil }