Add endpoint deletion
The endpoints screen could only create and edit servers, so a mistyped or retired endpoint stayed in the list forever. Tasks reference endpoints without ON DELETE CASCADE, so a referenced endpoint is refused with 409 and a count of the tasks using it rather than cascading away migration history. The foreign-key violation is mapped to the same status to cover a task created between check and delete. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,41 @@ func TestUpdateEndpoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteEndpoint(t *testing.T) {
|
||||
s := testStore(t)
|
||||
ctx := context.Background()
|
||||
id, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "src", Host: "a.com", Port: 993, TLSMode: "ssl"})
|
||||
if err := s.DeleteEndpoint(ctx, id); err != nil {
|
||||
t.Fatalf("delete: %v", err)
|
||||
}
|
||||
eps, _ := s.ListEndpoints(ctx)
|
||||
if len(eps) != 0 {
|
||||
t.Fatalf("endpoint not deleted: %d remain", len(eps))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteEndpointUsedByTaskRefused(t *testing.T) {
|
||||
s := testStore(t)
|
||||
ctx := context.Background()
|
||||
ep1, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "s", Host: "a", Port: 993, TLSMode: "ssl"})
|
||||
ep2, _ := s.CreateEndpoint(ctx, Endpoint{RoleLabel: "d", Host: "b", Port: 993, TLSMode: "ssl"})
|
||||
if _, err := s.CreateTask(ctx, Task{Name: "t", SrcEndpointID: ep1, DstEndpointID: ep2}); err != nil {
|
||||
t.Fatalf("create task: %v", err)
|
||||
}
|
||||
for _, id := range []int64{ep1, ep2} {
|
||||
n, err := s.CountTasksUsingEndpoint(ctx, id)
|
||||
if err != nil {
|
||||
t.Fatalf("count: %v", err)
|
||||
}
|
||||
if n != 1 {
|
||||
t.Fatalf("count for ep %d = %d, want 1", id, n)
|
||||
}
|
||||
if err := s.DeleteEndpoint(ctx, id); err == nil {
|
||||
t.Fatalf("delete of referenced endpoint %d succeeded, want FK violation", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAccountCascadesJournal(t *testing.T) {
|
||||
s := testStore(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -26,6 +26,23 @@ func (s *Store) UpdateEndpoint(ctx context.Context, e Endpoint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CountTasksUsingEndpoint reports how many tasks reference the endpoint on
|
||||
// either side, so a delete can be refused with a meaningful message instead of
|
||||
// surfacing a raw foreign-key violation.
|
||||
func (s *Store) CountTasksUsingEndpoint(ctx context.Context, id int64) (int, error) {
|
||||
var n int
|
||||
err := s.Pool.QueryRow(ctx,
|
||||
`SELECT count(*) FROM tasks WHERE src_endpoint_id=$1 OR dst_endpoint_id=$1`, id).Scan(&n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// DeleteEndpoint removes an endpoint. Tasks reference endpoints without ON
|
||||
// DELETE CASCADE, so Postgres rejects the delete while any task still uses it.
|
||||
func (s *Store) DeleteEndpoint(ctx context.Context, id int64) error {
|
||||
_, err := s.Pool.Exec(ctx, `DELETE FROM endpoints WHERE id=$1`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) GetEndpoint(ctx context.Context, id int64) (Endpoint, error) {
|
||||
var e Endpoint
|
||||
err := s.Pool.QueryRow(ctx,
|
||||
|
||||
Reference in New Issue
Block a user