Skip to content

Commit f458d89

Browse files
authored
feat: add disconnect api endpoints (#67)
1 parent 4259fdf commit f458d89

2 files changed

Lines changed: 65 additions & 15 deletions

File tree

cmd/onecli/apps.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,48 @@ func (c *AppsRemoveCmd) Run(out *output.Writer) error {
160160

161161
// AppsDisconnectCmd is `onecli apps disconnect`.
162162
type AppsDisconnectCmd struct {
163-
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
164-
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
163+
Provider string `required:"" help:"Provider name (e.g. 'github', 'gmail')."`
164+
ConnectionID string `optional:"" name:"connection-id" help:"Connection ID to disconnect (required if multiple connections exist)."`
165+
DryRun bool `optional:"" name:"dry-run" help:"Validate the request without executing it."`
165166
}
166167

167168
func (c *AppsDisconnectCmd) Run(out *output.Writer) error {
168169
if err := validate.ResourceID(c.Provider); err != nil {
169170
return fmt.Errorf("invalid provider: %w", err)
170171
}
171-
if c.DryRun {
172-
return out.WriteDryRun("Would disconnect app", map[string]string{"provider": c.Provider})
173-
}
174172
client, err := newClient()
175173
if err != nil {
176174
return err
177175
}
178-
if err := client.DisconnectApp(newContext(), c.Provider); err != nil {
176+
177+
connectionID := c.ConnectionID
178+
if connectionID == "" {
179+
connections, err := client.ListConnectionsByProvider(newContext(), c.Provider)
180+
if err != nil {
181+
return err
182+
}
183+
if len(connections) == 0 {
184+
return fmt.Errorf("no connections found for %s", c.Provider)
185+
}
186+
if len(connections) > 1 {
187+
out.Stderr(fmt.Sprintf("Multiple connections found for %s:", c.Provider))
188+
for _, conn := range connections {
189+
label := conn.Label
190+
if label == "" {
191+
label = conn.ID
192+
}
193+
out.Stderr(fmt.Sprintf(" %s %s (%s)", conn.ID, label, conn.Status))
194+
}
195+
return fmt.Errorf("specify --connection-id to disconnect a specific connection")
196+
}
197+
connectionID = connections[0].ID
198+
}
199+
200+
if c.DryRun {
201+
return out.WriteDryRun("Would disconnect app", map[string]string{"provider": c.Provider, "connectionId": connectionID})
202+
}
203+
if err := client.DisconnectApp(newContext(), connectionID); err != nil {
179204
return err
180205
}
181-
return out.Write(map[string]string{"status": "disconnected", "provider": c.Provider})
206+
return out.Write(map[string]string{"status": "disconnected", "provider": c.Provider, "connectionId": connectionID})
182207
}

internal/api/apps.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type AppConfig struct {
2626

2727
// AppConnection is the OAuth connection status.
2828
type AppConnection struct {
29+
ID string `json:"id"`
30+
Provider string `json:"provider"`
31+
Label string `json:"label,omitempty"`
2932
Status string `json:"status"`
3033
Scopes []string `json:"scopes"`
3134
ConnectedAt string `json:"connectedAt"`
@@ -55,6 +58,36 @@ func (c *Client) GetApp(ctx context.Context, provider string) (*App, error) {
5558
return &app, nil
5659
}
5760

61+
// ListConnections returns all app connections for the current project.
62+
func (c *Client) ListConnections(ctx context.Context) ([]AppConnection, error) {
63+
var resp struct {
64+
Connections []AppConnection `json:"connections"`
65+
}
66+
if err := c.do(ctx, http.MethodGet, "/v1/apps/connections", nil, &resp); err != nil {
67+
return nil, fmt.Errorf("listing connections: %w", err)
68+
}
69+
return resp.Connections, nil
70+
}
71+
72+
// ListConnectionsByProvider returns app connections for a specific provider.
73+
func (c *Client) ListConnectionsByProvider(ctx context.Context, provider string) ([]AppConnection, error) {
74+
var resp struct {
75+
Connections []AppConnection `json:"connections"`
76+
}
77+
if err := c.do(ctx, http.MethodGet, "/v1/apps/connections/"+provider, nil, &resp); err != nil {
78+
return nil, fmt.Errorf("listing connections for %s: %w", provider, err)
79+
}
80+
return resp.Connections, nil
81+
}
82+
83+
// DisconnectApp removes an app connection by ID.
84+
func (c *Client) DisconnectApp(ctx context.Context, connectionID string) error {
85+
if err := c.do(ctx, http.MethodDelete, "/v1/apps/connections/"+connectionID, nil, nil); err != nil {
86+
return fmt.Errorf("disconnecting app: %w", err)
87+
}
88+
return nil
89+
}
90+
5891
// ConfigureApp saves BYOC credentials for a provider.
5992
func (c *Client) ConfigureApp(ctx context.Context, provider string, input ConfigAppInput) error {
6093
var resp SuccessResponse
@@ -71,11 +104,3 @@ func (c *Client) UnconfigureApp(ctx context.Context, provider string) error {
71104
}
72105
return nil
73106
}
74-
75-
// DisconnectApp removes the OAuth connection for a provider.
76-
func (c *Client) DisconnectApp(ctx context.Context, provider string) error {
77-
if err := c.do(ctx, http.MethodDelete, "/v1/apps/"+provider+"/connection", nil, nil); err != nil {
78-
return fmt.Errorf("disconnecting app: %w", err)
79-
}
80-
return nil
81-
}

0 commit comments

Comments
 (0)