Skip to content

Commit 49c4b5e

Browse files
committed
Show attached apps when listing MPGs
1 parent e3da867 commit 49c4b5e

File tree

3 files changed

+139
-1
lines changed

3 files changed

+139
-1
lines changed

internal/command/mpg/list.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package mpg
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
"github.com/spf13/cobra"
89

910
"github.com/superfly/flyctl/gql"
11+
"github.com/superfly/flyctl/internal/uiex"
1012
"github.com/superfly/flyctl/iostreams"
1113

1214
"github.com/superfly/flyctl/internal/command"
@@ -95,8 +97,22 @@ func runList(ctx context.Context) error {
9597
cluster.Region,
9698
cluster.Status,
9799
cluster.Plan,
100+
formatAttachedApps(cluster.AttachedApps),
98101
})
99102
}
100103

101-
return render.Table(out, "", rows, "ID", "Name", "Org", "Region", "Status", "Plan")
104+
return render.Table(out, "", rows, "ID", "Name", "Org", "Region", "Status", "Plan", "Attached Apps")
105+
}
106+
107+
// formatAttachedApps formats the list of attached apps for display
108+
func formatAttachedApps(apps []uiex.AttachedApp) string {
109+
if len(apps) == 0 {
110+
return "<no attached apps>"
111+
}
112+
113+
names := make([]string, len(apps))
114+
for i, app := range apps {
115+
names[i] = app.Name
116+
}
117+
return strings.Join(names, ", ")
102118
}

internal/command/mpg/mpg_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,3 +1486,119 @@ func TestInvalidPGMajorVersion_Error(t *testing.T) {
14861486
})
14871487
}
14881488
}
1489+
1490+
// Test formatAttachedApps function
1491+
func TestFormatAttachedApps(t *testing.T) {
1492+
tests := []struct {
1493+
name string
1494+
apps []uiex.AttachedApp
1495+
expected string
1496+
}{
1497+
{
1498+
name: "no attached apps",
1499+
apps: []uiex.AttachedApp{},
1500+
expected: "<no attached apps>",
1501+
},
1502+
{
1503+
name: "nil apps",
1504+
apps: nil,
1505+
expected: "<no attached apps>",
1506+
},
1507+
{
1508+
name: "single app",
1509+
apps: []uiex.AttachedApp{
1510+
{Name: "my-web-app", Id: 1},
1511+
},
1512+
expected: "my-web-app",
1513+
},
1514+
{
1515+
name: "two apps",
1516+
apps: []uiex.AttachedApp{
1517+
{Name: "my-web-app", Id: 1},
1518+
{Name: "my-api", Id: 2},
1519+
},
1520+
expected: "my-web-app, my-api",
1521+
},
1522+
{
1523+
name: "three apps",
1524+
apps: []uiex.AttachedApp{
1525+
{Name: "app-one", Id: 1},
1526+
{Name: "app-two", Id: 2},
1527+
{Name: "app-three", Id: 3},
1528+
},
1529+
expected: "app-one, app-two, app-three",
1530+
},
1531+
}
1532+
1533+
for _, tt := range tests {
1534+
t.Run(tt.name, func(t *testing.T) {
1535+
result := formatAttachedApps(tt.apps)
1536+
assert.Equal(t, tt.expected, result)
1537+
})
1538+
}
1539+
}
1540+
1541+
// Test the list command with attached apps
1542+
func TestListCommand_WithAttachedApps(t *testing.T) {
1543+
ctx := setupTestContext()
1544+
1545+
expectedClusters := []uiex.ManagedCluster{
1546+
{
1547+
Id: "cluster-1",
1548+
Name: "test-cluster-1",
1549+
Region: "ord",
1550+
Status: "ready",
1551+
Plan: "development",
1552+
Organization: fly.Organization{
1553+
Slug: "test-org",
1554+
},
1555+
AttachedApps: []uiex.AttachedApp{
1556+
{Name: "web-app", Id: 100},
1557+
{Name: "api-app", Id: 101},
1558+
},
1559+
},
1560+
{
1561+
Id: "cluster-2",
1562+
Name: "test-cluster-2",
1563+
Region: "lax",
1564+
Status: "ready",
1565+
Plan: "production",
1566+
Organization: fly.Organization{
1567+
Slug: "test-org",
1568+
},
1569+
AttachedApps: []uiex.AttachedApp{}, // No attached apps
1570+
},
1571+
}
1572+
1573+
mockUiex := &mock.UiexClient{
1574+
ListManagedClustersFunc: func(ctx context.Context, orgSlug string, deleted bool) (uiex.ListManagedClustersResponse, error) {
1575+
assert.Equal(t, "test-org", orgSlug)
1576+
return uiex.ListManagedClustersResponse{
1577+
Data: expectedClusters,
1578+
}, nil
1579+
},
1580+
}
1581+
1582+
ctx = uiexutil.NewContextWithClient(ctx, mockUiex)
1583+
1584+
// Test successful cluster listing with attached apps
1585+
clusters, err := mockUiex.ListManagedClusters(ctx, "test-org", false)
1586+
require.NoError(t, err)
1587+
assert.Len(t, clusters.Data, 2)
1588+
1589+
// Verify first cluster has attached apps
1590+
assert.Len(t, clusters.Data[0].AttachedApps, 2)
1591+
assert.Equal(t, "web-app", clusters.Data[0].AttachedApps[0].Name)
1592+
assert.Equal(t, "api-app", clusters.Data[0].AttachedApps[1].Name)
1593+
1594+
// Verify attached apps formatting for first cluster
1595+
formattedApps := formatAttachedApps(clusters.Data[0].AttachedApps)
1596+
assert.Equal(t, "web-app, api-app", formattedApps)
1597+
1598+
// Verify second cluster has no attached apps
1599+
assert.Len(t, clusters.Data[1].AttachedApps, 0)
1600+
1601+
// Verify attached apps formatting for second cluster (empty)
1602+
formattedApps = formatAttachedApps(clusters.Data[1].AttachedApps)
1603+
assert.Equal(t, "<no attached apps>", formattedApps)
1604+
}

internal/uiex/managed_postgres.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ type RestoreManagedClusterBackupResponse struct {
5454
Data ManagedCluster `json:"data"`
5555
}
5656

57+
type AttachedApp struct {
58+
Name string `json:"name"`
59+
Id int64 `json:"id"`
60+
}
61+
5762
type ManagedCluster struct {
5863
Id string `json:"id"`
5964
Name string `json:"name"`
@@ -64,6 +69,7 @@ type ManagedCluster struct {
6469
Replicas int `json:"replicas"`
6570
Organization fly.Organization `json:"organization"`
6671
IpAssignments ManagedClusterIpAssignments `json:"ip_assignments"`
72+
AttachedApps []AttachedApp `json:"attached_apps"`
6773
}
6874

6975
type ListManagedClustersResponse struct {

0 commit comments

Comments
 (0)