Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions internal/tiger/cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ func buildDbConnectionStringCmd() *cobra.Command {
var dbConnectionStringWithPassword bool

cmd := &cobra.Command{
Use: "connection-string [service-id]",
Short: "Get connection string for a service",
Use: "connection-string [service-id]",
Short: "Get connection string for a service",
ValidArgsFunction: serviceIDCompletion,
Long: `Get a PostgreSQL connection string for connecting to a database service.

The service ID can be provided as an argument or will use the default service
Expand Down Expand Up @@ -117,9 +118,10 @@ func buildDbConnectCmd() *cobra.Command {
var dbConnectRole string

cmd := &cobra.Command{
Use: "connect [service-id]",
Aliases: []string{"psql"},
Short: "Connect to a database",
Use: "connect [service-id]",
Aliases: []string{"psql"},
Short: "Connect to a database",
ValidArgsFunction: serviceIDCompletion,
Long: `Connect to a database service using psql client.

The service ID can be provided as an argument or will use the default service
Expand Down Expand Up @@ -196,8 +198,9 @@ func buildDbTestConnectionCmd() *cobra.Command {
var dbTestConnectionRole string

cmd := &cobra.Command{
Use: "test-connection [service-id]",
Short: "Test database connectivity",
Use: "test-connection [service-id]",
Short: "Test database connectivity",
ValidArgsFunction: serviceIDCompletion,
Long: `Test database connectivity to a service.

The service ID can be provided as an argument or will use the default service
Expand Down Expand Up @@ -268,8 +271,9 @@ func buildDbSavePasswordCmd() *cobra.Command {
var dbSavePasswordValue string

cmd := &cobra.Command{
Use: "save-password [service-id]",
Short: "Save password for a database service",
Use: "save-password [service-id]",
Short: "Save password for a database service",
ValidArgsFunction: serviceIDCompletion,
Long: `Save a password for a database service to configured password storage.

The service ID can be provided as an argument or will use the default service
Expand Down Expand Up @@ -574,8 +578,9 @@ func buildDbCreateRoleCmd() *cobra.Command {
var output string

cmd := &cobra.Command{
Use: "role [service-id]",
Short: "Create a new database role",
Use: "role [service-id]",
Short: "Create a new database role",
ValidArgsFunction: serviceIDCompletion,
Long: `Create a new database role with optional read-only enforcement.

The service ID can be provided as an argument or will use the default service
Expand Down
54 changes: 53 additions & 1 deletion internal/tiger/cmd/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Examples:

# Get service details in YAML format
tiger service get svc-12345 --output yaml`,
ValidArgsFunction: serviceIDCompletion,
RunE: func(cmd *cobra.Command, args []string) error {
// Get config
cfg, err := config.Load()
Expand Down Expand Up @@ -470,6 +471,7 @@ Examples:

# Update password without saving (using global flag)
tiger service update-password svc-12345 --new-password new-secure-password --password-storage none`,
ValidArgsFunction: serviceIDCompletion,
RunE: func(cmd *cobra.Command, args []string) error {
// Get config
cfg, err := config.Load()
Expand Down Expand Up @@ -901,6 +903,7 @@ Examples:

# Delete service with custom wait timeout
tiger service delete svc-12345 --wait-timeout 15m`,
ValidArgsFunction: serviceIDCompletion,
RunE: func(cmd *cobra.Command, args []string) error {
// Require explicit service ID for safety
if len(args) < 1 {
Expand Down Expand Up @@ -1092,7 +1095,8 @@ Examples:

# Fork with custom wait timeout
tiger service fork svc-12345 --now --wait-timeout 45m`,
Args: cobra.MaximumNArgs(1),
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: serviceIDCompletion,
RunE: func(cmd *cobra.Command, args []string) error {
// Validate timing flags first - exactly one must be specified
timingFlagsSet := 0
Expand Down Expand Up @@ -1287,3 +1291,51 @@ Examples:

return cmd
}
func serviceIDCompletion(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
services, err := listServices(cmd)
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

results := make([]string, 0, len(services))
for _, service := range services {
if service.ServiceId != nil && strings.HasPrefix(*service.ServiceId, toComplete) {
results = append(results, cobra.CompletionWithDesc(*service.ServiceId, *service.Name))
}
}
return results, cobra.ShellCompDirectiveNoFileComp
}

func listServices(cmd *cobra.Command) ([]api.Service, error) {
// Get API key and project ID for authentication
apiKey, projectID, err := getCredentialsForService()
if err != nil {
return nil, exitWithCode(ExitAuthenticationError, fmt.Errorf("authentication required: %w. Please run 'tiger auth login'", err))
}

// Create API client
client, err := api.NewTigerClient(apiKey)
if err != nil {
return nil, fmt.Errorf("failed to create API client: %w", err)
}

// Make API call to list services
ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Second)
defer cancel()

resp, err := client.GetProjectsProjectIdServicesWithResponse(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("failed to list services: %w", err)
}

// Handle API response
if resp.StatusCode() != 200 {
return nil, exitWithErrorFromStatusCode(resp.StatusCode(), resp.JSON4XX)
}

if resp.JSON200 == nil || len(*resp.JSON200) == 0 {
return []api.Service{}, nil
}

return *resp.JSON200, nil
}