Files
kandev/apps/backend/internal/github/service_task_pr_detach.go
T

87 lines
2.6 KiB
Go

package github
import (
"context"
"errors"
"strings"
"github.com/kandev/kandev/internal/events"
"github.com/kandev/kandev/internal/events/bus"
taskmodels "github.com/kandev/kandev/internal/task/models"
"go.uber.org/zap"
)
var ErrTaskPRNotFound = errors.New("github task PR not found")
// TaskPRDeletedEvent identifies one task-PR association removed from active
// task surfaces. The remote GitHub pull request is intentionally untouched.
type TaskPRDeletedEvent struct {
WorkspaceID string `json:"workspace_id"`
TaskID string `json:"task_id"`
AssociationID string `json:"association_id"`
}
// GetWorkspaceID lets the websocket broadcaster route the typed event to the
// owning workspace instead of treating it as an instance-wide notification.
func (e TaskPRDeletedEvent) GetWorkspaceID() string { return e.WorkspaceID }
// DetachTaskPR removes one association from active task views while retaining
// a durable tombstone that prevents background discovery from resurrecting it.
func (s *Service) DetachTaskPR(ctx context.Context, workspaceID, associationID string) (*TaskPR, error) {
if strings.TrimSpace(workspaceID) == "" || strings.TrimSpace(associationID) == "" {
return nil, ErrTaskPRNotFound
}
tp, err := s.store.GetTaskPRByID(ctx, associationID)
if err != nil {
return nil, err
}
if tp == nil || (tp.WorkspaceID != "" && tp.WorkspaceID != workspaceID) {
return nil, ErrTaskPRNotFound
}
if err := s.authorizeWorkspaceAccess(ctx, workspaceID); err != nil {
return nil, err
}
if s.comparisonTargetObserver != nil && tp.RepositoryID != "" {
if err := s.comparisonTargetObserver.RemoveComparisonTargetForChange(
ctx,
tp.TaskID,
tp.RepositoryID,
taskmodels.ComparisonTargetProviderGitHub,
taskmodels.ComparisonTargetKindPullRequest,
tp.PRNumber,
); err != nil {
if s.logger != nil {
s.logger.Warn("comparison target detach cleanup failed",
zap.String("task_id", tp.TaskID),
zap.Int("pr_number", tp.PRNumber),
zap.Error(err))
}
return nil, err
}
}
if tp.DetachedAt != nil {
return tp, nil
}
detached, transitioned, err := s.store.DetachTaskPR(ctx, associationID)
if err != nil {
return nil, err
}
if detached == nil {
return nil, ErrTaskPRNotFound
}
if !transitioned {
return detached, nil
}
if s.eventBus != nil {
event := bus.NewEvent(events.GitHubTaskPRDeleted, "github", &TaskPRDeletedEvent{
WorkspaceID: workspaceID,
TaskID: detached.TaskID,
AssociationID: detached.ID,
})
if err := s.eventBus.Publish(ctx, events.GitHubTaskPRDeleted, event); err != nil {
s.logger.Debug("failed to publish task PR deleted event", zap.Error(err))
}
}
return detached, nil
}