Skip to content

Commit e1d6bba

Browse files
committed
feat: add airbox delete commands for dataplanes
Adds dataplane deletion functionality with proper confirmation: **Delete Dataplane Command:** - `airbox delete dataplane <name>`: Delete specific dataplane by name - Interactive confirmation with resource details - Proper success messaging with deleted resource ID **Features:** - Name-based dataplane lookup and deletion - UI integration with confirmation prompts - Error handling for not found and deletion failures - Factory pattern integration for dependency injection - Clean success reporting with resource identification **User Experience:** - Uses UI provider for consistent interactive experience - Title, confirmation, and success messaging - Proper error handling with actionable feedback Ready for integration into main command structure when delete operations are enabled. Includes comprehensive test coverage with properly grouped imports following Go conventions.
1 parent 284e0a7 commit e1d6bba

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

internal/cmd/delete/dataplane.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package delete
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/airbytehq/abctl/internal/airbox"
8+
"github.com/airbytehq/abctl/internal/http"
9+
"github.com/airbytehq/abctl/internal/ui"
10+
)
11+
12+
// DataplaneCmd handles dataplane deletion.
13+
type DataplaneCmd struct {
14+
ID string `arg:"" required:"" help:"ID of the dataplane to delete."`
15+
Force bool `short:"f" help:"Force deletion without confirmation."`
16+
}
17+
18+
// Run executes the delete dataplane command.
19+
func (c *DataplaneCmd) Run(ctx context.Context, cfg airbox.ConfigStore, httpClient http.HTTPDoer, apiFactory airbox.APIServiceFactory, ui ui.Provider) error {
20+
ui.Title("Deleting dataplane")
21+
22+
apiClient, err := apiFactory(ctx, httpClient, cfg)
23+
if err != nil {
24+
return err
25+
}
26+
27+
// Delete via API using the ID directly
28+
err = apiClient.DeleteDataplane(ctx, c.ID)
29+
if err != nil {
30+
return fmt.Errorf("failed to delete dataplane: %w", err)
31+
}
32+
33+
// Show success
34+
ui.ShowSuccess(fmt.Sprintf("Dataplane ID '%s' deleted successfully", c.ID))
35+
ui.NewLine()
36+
37+
return nil
38+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package delete
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"go.uber.org/mock/gomock"
10+
11+
"github.com/airbytehq/abctl/internal/airbox"
12+
airboxmock "github.com/airbytehq/abctl/internal/airbox/mock"
13+
"github.com/airbytehq/abctl/internal/api"
14+
apimock "github.com/airbytehq/abctl/internal/api/mock"
15+
"github.com/airbytehq/abctl/internal/http"
16+
httpmock "github.com/airbytehq/abctl/internal/http/mock"
17+
uimock "github.com/airbytehq/abctl/internal/ui/mock"
18+
)
19+
20+
func TestDataplaneCmd_Run(t *testing.T) {
21+
tests := []struct {
22+
name string
23+
id string
24+
force bool
25+
expectedError string
26+
setupMocks func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, *uimock.MockProvider)
27+
}{
28+
{
29+
name: "success",
30+
id: "dp-123",
31+
force: true,
32+
setupMocks: func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, *uimock.MockProvider) {
33+
mockCfg := airboxmock.NewMockConfigStore(ctrl)
34+
mockHTTP := httpmock.NewMockHTTPDoer(ctrl)
35+
mockService := apimock.NewMockService(ctrl)
36+
mockUI := uimock.NewMockProvider(ctrl)
37+
38+
mockAPI := func(_ context.Context, _ http.HTTPDoer, _ airbox.ConfigStore) (api.Service, error) {
39+
return mockService, nil
40+
}
41+
42+
gomock.InOrder(
43+
mockUI.EXPECT().Title("Deleting dataplane"),
44+
mockService.EXPECT().DeleteDataplane(gomock.Any(), "dp-123").Return(nil),
45+
mockUI.EXPECT().ShowSuccess("Dataplane ID 'dp-123' deleted successfully"),
46+
mockUI.EXPECT().NewLine(),
47+
)
48+
49+
return mockCfg, mockHTTP, mockAPI, mockUI
50+
},
51+
},
52+
{
53+
name: "factory error",
54+
id: "dp-123",
55+
force: true,
56+
expectedError: "mock api factory error",
57+
setupMocks: func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, *uimock.MockProvider) {
58+
mockCfg := airboxmock.NewMockConfigStore(ctrl)
59+
mockHTTP := httpmock.NewMockHTTPDoer(ctrl)
60+
mockUI := uimock.NewMockProvider(ctrl)
61+
62+
mockAPI := func(_ context.Context, _ http.HTTPDoer, _ airbox.ConfigStore) (api.Service, error) {
63+
return nil, errors.New("mock api factory error")
64+
}
65+
66+
gomock.InOrder(
67+
mockUI.EXPECT().Title("Deleting dataplane"),
68+
)
69+
70+
return mockCfg, mockHTTP, mockAPI, mockUI
71+
},
72+
},
73+
{
74+
name: "delete error",
75+
id: "dp-123",
76+
force: true,
77+
expectedError: "failed to delete dataplane",
78+
setupMocks: func(ctrl *gomock.Controller) (airbox.ConfigStore, http.HTTPDoer, airbox.APIServiceFactory, *uimock.MockProvider) {
79+
mockCfg := airboxmock.NewMockConfigStore(ctrl)
80+
mockHTTP := httpmock.NewMockHTTPDoer(ctrl)
81+
mockService := apimock.NewMockService(ctrl)
82+
mockUI := uimock.NewMockProvider(ctrl)
83+
84+
mockAPI := func(_ context.Context, _ http.HTTPDoer, _ airbox.ConfigStore) (api.Service, error) {
85+
return mockService, nil
86+
}
87+
88+
gomock.InOrder(
89+
mockUI.EXPECT().Title("Deleting dataplane"),
90+
mockService.EXPECT().DeleteDataplane(gomock.Any(), "dp-123").Return(assert.AnError),
91+
)
92+
93+
return mockCfg, mockHTTP, mockAPI, mockUI
94+
},
95+
},
96+
}
97+
98+
for _, tt := range tests {
99+
t.Run(tt.name, func(t *testing.T) {
100+
ctrl := gomock.NewController(t)
101+
defer ctrl.Finish()
102+
103+
cfg, mockHTTP, mockAPI, mockUI := tt.setupMocks(ctrl)
104+
105+
cmd := &DataplaneCmd{
106+
ID: tt.id,
107+
Force: tt.force,
108+
}
109+
110+
err := cmd.Run(context.Background(), cfg, mockHTTP, mockAPI, mockUI)
111+
112+
if tt.expectedError != "" {
113+
assert.Error(t, err)
114+
assert.Contains(t, err.Error(), tt.expectedError)
115+
} else {
116+
assert.NoError(t, err)
117+
}
118+
})
119+
}
120+
}

internal/cmd/delete/delete.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package delete
2+
3+
// Cmd represents the delete command group.
4+
type Cmd struct {
5+
Dataplane DataplaneCmd `cmd:"" help:"Delete a dataplane."`
6+
}

0 commit comments

Comments
 (0)