fixed blocking issues
This commit is contained in:
+184
-84
@@ -13,25 +13,25 @@ import (
|
||||
type Service interface {
|
||||
// Board operations
|
||||
CreateBoard(ctx context.Context, req CreateBoardRequest) (*BoardResponse, error)
|
||||
GetBoardByID(ctx context.Context, id string) (*BoardDetailResponse, error)
|
||||
GetBoardByID(ctx context.Context, id, ownerID string) (*BoardDetailResponse, error)
|
||||
GetBoardsByUserID(ctx context.Context, userID string) (*BoardsListResponse, error)
|
||||
GetDefaultBoard(ctx context.Context, userID string) (*BoardDetailResponse, error)
|
||||
UpdateBoard(ctx context.Context, req UpdateBoardRequest) (*BoardResponse, error)
|
||||
DeleteBoard(ctx context.Context, id string) error
|
||||
UpdateBoard(ctx context.Context, req UpdateBoardRequest, ownerID string) (*BoardResponse, error)
|
||||
DeleteBoard(ctx context.Context, id, ownerID string) error
|
||||
|
||||
// Column operations
|
||||
CreateColumn(ctx context.Context, req CreateColumnRequest) (*ColumnResponse, error)
|
||||
UpdateColumn(ctx context.Context, req UpdateColumnRequest) (*ColumnResponse, error)
|
||||
DeleteColumn(ctx context.Context, id string) error
|
||||
ReorderColumns(ctx context.Context, req ReorderColumnsRequest) error
|
||||
CreateColumn(ctx context.Context, req CreateColumnRequest, ownerID string) (*ColumnResponse, error)
|
||||
UpdateColumn(ctx context.Context, req UpdateColumnRequest, ownerID string) (*ColumnResponse, error)
|
||||
DeleteColumn(ctx context.Context, id, ownerID string) error
|
||||
ReorderColumns(ctx context.Context, req ReorderColumnsRequest, ownerID string) error
|
||||
|
||||
// Card operations
|
||||
CreateCard(ctx context.Context, req CreateCardRequest) (*CardResponse, error)
|
||||
GetCardByID(ctx context.Context, id string) (*CardResponse, error)
|
||||
UpdateCard(ctx context.Context, req UpdateCardRequest) (*CardResponse, error)
|
||||
DeleteCard(ctx context.Context, id string) error
|
||||
MoveCard(ctx context.Context, req MoveCardRequest) error
|
||||
ReorderCards(ctx context.Context, req ReorderCardsRequest) error
|
||||
CreateCard(ctx context.Context, req CreateCardRequest, ownerID string) (*CardResponse, error)
|
||||
GetCardByID(ctx context.Context, id, ownerID string) (*CardResponse, error)
|
||||
UpdateCard(ctx context.Context, req UpdateCardRequest, ownerID string) (*CardResponse, error)
|
||||
DeleteCard(ctx context.Context, id, ownerID string) error
|
||||
MoveCard(ctx context.Context, req MoveCardRequest, ownerID string) error
|
||||
ReorderCards(ctx context.Context, req ReorderCardsRequest, ownerID string) error
|
||||
}
|
||||
|
||||
// kanbanService implements KanbanService interface
|
||||
@@ -96,12 +96,13 @@ func (s *kanbanService) CreateBoard(ctx context.Context, req CreateBoardRequest)
|
||||
}
|
||||
|
||||
// GetBoardByID retrieves a board with full column and card details
|
||||
func (s *kanbanService) GetBoardByID(ctx context.Context, id string) (*BoardDetailResponse, error) {
|
||||
func (s *kanbanService) GetBoardByID(ctx context.Context, id, ownerID string) (*BoardDetailResponse, error) {
|
||||
s.logger.Info("Retrieving board by ID", map[string]interface{}{
|
||||
"board_id": id,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
board, err := s.boardRepository.GetBoardByID(ctx, id)
|
||||
board, err := s.getBoardForOwner(ctx, id, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,16 +181,17 @@ func (s *kanbanService) GetDefaultBoard(ctx context.Context, userID string) (*Bo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.GetBoardByID(ctx, board.ID.Hex())
|
||||
return s.GetBoardByID(ctx, board.ID.Hex(), userID)
|
||||
}
|
||||
|
||||
// UpdateBoard updates an existing board
|
||||
func (s *kanbanService) UpdateBoard(ctx context.Context, req UpdateBoardRequest) (*BoardResponse, error) {
|
||||
func (s *kanbanService) UpdateBoard(ctx context.Context, req UpdateBoardRequest, ownerID string) (*BoardResponse, error) {
|
||||
s.logger.Info("Updating board", map[string]interface{}{
|
||||
"board_id": req.ID,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
board, err := s.boardRepository.GetBoardByID(ctx, req.ID)
|
||||
board, err := s.getBoardForOwner(ctx, req.ID, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,11 +224,16 @@ func (s *kanbanService) UpdateBoard(ctx context.Context, req UpdateBoardRequest)
|
||||
}
|
||||
|
||||
// DeleteBoard deletes a board and all its columns and cards
|
||||
func (s *kanbanService) DeleteBoard(ctx context.Context, id string) error {
|
||||
func (s *kanbanService) DeleteBoard(ctx context.Context, id, ownerID string) error {
|
||||
s.logger.Info("Deleting board", map[string]interface{}{
|
||||
"board_id": id,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
if _, err := s.getBoardForOwner(ctx, id, ownerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all columns for this board
|
||||
columns, err := s.columnRepository.GetColumnsByBoardID(ctx, id)
|
||||
if err != nil {
|
||||
@@ -267,40 +274,46 @@ func (s *kanbanService) DeleteBoard(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
// CreateColumn creates a new column in a board
|
||||
func (s *kanbanService) CreateColumn(ctx context.Context, req CreateColumnRequest) (*ColumnResponse, error) {
|
||||
func (s *kanbanService) CreateColumn(ctx context.Context, req CreateColumnRequest, ownerID string) (*ColumnResponse, error) {
|
||||
s.logger.Info("Creating new column", map[string]interface{}{
|
||||
"name": req.Name,
|
||||
"board_id": req.BoardID,
|
||||
"order": req.Order,
|
||||
})
|
||||
|
||||
if _, err := s.getBoardForOwner(ctx, req.BoardID, ownerID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("kanban board uses a fixed bid workflow; columns are created automatically from GoRules")
|
||||
}
|
||||
|
||||
// UpdateColumn updates an existing column
|
||||
func (s *kanbanService) UpdateColumn(ctx context.Context, req UpdateColumnRequest) (*ColumnResponse, error) {
|
||||
func (s *kanbanService) UpdateColumn(ctx context.Context, req UpdateColumnRequest, ownerID string) (*ColumnResponse, error) {
|
||||
s.logger.Info("Updating column", map[string]interface{}{
|
||||
"column_id": req.ID,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
column, err := s.columnRepository.GetColumnByID(ctx, req.ID)
|
||||
column, _, err := s.getColumnForOwner(ctx, req.ID, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if req.Name != nil {
|
||||
return nil, fmt.Errorf("column name cannot be changed for fixed status boards")
|
||||
}
|
||||
if req.Color != nil {
|
||||
column.Color = *req.Color
|
||||
return nil, fmt.Errorf("column name cannot be changed on a fixed workflow board")
|
||||
}
|
||||
if req.Order != nil {
|
||||
column.Order = *req.Order
|
||||
return nil, fmt.Errorf("column order cannot be changed on a fixed workflow board")
|
||||
}
|
||||
if req.Limit != nil {
|
||||
column.Limit = req.Limit
|
||||
return nil, fmt.Errorf("column WIP limit cannot be changed on a fixed workflow board")
|
||||
}
|
||||
if req.Color == nil {
|
||||
return nil, fmt.Errorf("only column color can be updated on a fixed workflow board")
|
||||
}
|
||||
|
||||
column.Color = *req.Color
|
||||
|
||||
if err := s.columnRepository.UpdateColumn(ctx, column); err != nil {
|
||||
return nil, err
|
||||
@@ -310,7 +323,7 @@ func (s *kanbanService) UpdateColumn(ctx context.Context, req UpdateColumnReques
|
||||
}
|
||||
|
||||
// DeleteColumn deletes a column and moves all its cards to the next column
|
||||
func (s *kanbanService) DeleteColumn(ctx context.Context, id string) error {
|
||||
func (s *kanbanService) DeleteColumn(ctx context.Context, id, ownerID string) error {
|
||||
s.logger.Info("Deleting column", map[string]interface{}{
|
||||
"column_id": id,
|
||||
})
|
||||
@@ -319,28 +332,31 @@ func (s *kanbanService) DeleteColumn(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
// ReorderColumns reorders columns within a board
|
||||
func (s *kanbanService) ReorderColumns(ctx context.Context, req ReorderColumnsRequest) error {
|
||||
func (s *kanbanService) ReorderColumns(ctx context.Context, req ReorderColumnsRequest, ownerID string) error {
|
||||
s.logger.Info("Reordering columns", map[string]interface{}{
|
||||
"board_id": req.BoardID,
|
||||
"count": len(req.ColumnOrders),
|
||||
})
|
||||
|
||||
if _, err := s.getBoardForOwner(ctx, req.BoardID, ownerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("columns are fixed and cannot be reordered")
|
||||
}
|
||||
|
||||
// CreateCard creates a new card in a column
|
||||
func (s *kanbanService) CreateCard(ctx context.Context, req CreateCardRequest) (*CardResponse, error) {
|
||||
func (s *kanbanService) CreateCard(ctx context.Context, req CreateCardRequest, ownerID string) (*CardResponse, error) {
|
||||
s.logger.Info("Creating new card", map[string]interface{}{
|
||||
"title": req.Title,
|
||||
"tender_id": req.TenderID,
|
||||
"column_id": req.ColumnID,
|
||||
"order": req.Order,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
// Validate that the column exists
|
||||
_, err := s.columnRepository.GetColumnByID(ctx, req.ColumnID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("column not found: %w", err)
|
||||
if _, _, err := s.getColumnForOwner(ctx, req.ColumnID, ownerID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check for duplicate card (same tender in same column)
|
||||
@@ -373,12 +389,13 @@ func (s *kanbanService) CreateCard(ctx context.Context, req CreateCardRequest) (
|
||||
}
|
||||
|
||||
// GetCardByID retrieves a card by ID
|
||||
func (s *kanbanService) GetCardByID(ctx context.Context, id string) (*CardResponse, error) {
|
||||
func (s *kanbanService) GetCardByID(ctx context.Context, id, ownerID string) (*CardResponse, error) {
|
||||
s.logger.Info("Retrieving card by ID", map[string]interface{}{
|
||||
"card_id": id,
|
||||
"card_id": id,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
card, err := s.cardRepository.GetCardByID(ctx, id)
|
||||
card, _, _, err := s.getCardForOwner(ctx, id, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -387,12 +404,13 @@ func (s *kanbanService) GetCardByID(ctx context.Context, id string) (*CardRespon
|
||||
}
|
||||
|
||||
// UpdateCard updates an existing card
|
||||
func (s *kanbanService) UpdateCard(ctx context.Context, req UpdateCardRequest) (*CardResponse, error) {
|
||||
func (s *kanbanService) UpdateCard(ctx context.Context, req UpdateCardRequest, ownerID string) (*CardResponse, error) {
|
||||
s.logger.Info("Updating card", map[string]interface{}{
|
||||
"card_id": req.ID,
|
||||
"card_id": req.ID,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
card, err := s.cardRepository.GetCardByID(ctx, req.ID)
|
||||
card, _, _, err := s.getCardForOwner(ctx, req.ID, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -419,39 +437,43 @@ func (s *kanbanService) UpdateCard(ctx context.Context, req UpdateCardRequest) (
|
||||
}
|
||||
|
||||
// DeleteCard deletes a card
|
||||
func (s *kanbanService) DeleteCard(ctx context.Context, id string) error {
|
||||
func (s *kanbanService) DeleteCard(ctx context.Context, id, ownerID string) error {
|
||||
s.logger.Info("Deleting card", map[string]interface{}{
|
||||
"card_id": id,
|
||||
"card_id": id,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
if _, _, _, err := s.getCardForOwner(ctx, id, ownerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.cardRepository.DeleteCard(ctx, id)
|
||||
}
|
||||
|
||||
// MoveCard moves a card to a different column
|
||||
func (s *kanbanService) MoveCard(ctx context.Context, req MoveCardRequest) error {
|
||||
func (s *kanbanService) MoveCard(ctx context.Context, req MoveCardRequest, ownerID string) error {
|
||||
s.logger.Info("Moving card", map[string]interface{}{
|
||||
"card_id": req.CardID,
|
||||
"column_id": req.ColumnID,
|
||||
"new_order": req.NewOrder,
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
// Validate that the target column exists
|
||||
targetColumn, err := s.columnRepository.GetColumnByID(ctx, req.ColumnID)
|
||||
card, currentColumn, board, err := s.getCardForOwner(ctx, req.CardID, ownerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("target column not found: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
card, err := s.cardRepository.GetCardByID(ctx, req.CardID)
|
||||
targetColumn, _, err := s.getColumnForOwner(ctx, req.ColumnID, ownerID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("card not found: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
currentColumn, err := s.columnRepository.GetColumnByID(ctx, card.ColumnID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("current column not found: %w", err)
|
||||
if targetColumn.BoardID != board.ID.Hex() {
|
||||
return ErrAccessDenied
|
||||
}
|
||||
|
||||
allowedStatuses, err := s.availableStatuses(ctx, currentColumn.Status)
|
||||
allowedStatuses, err := s.availableStatuses(ctx, board.ID.Hex(), currentColumn.Status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -461,16 +483,22 @@ func (s *kanbanService) MoveCard(ctx context.Context, req MoveCardRequest) error
|
||||
return fmt.Errorf("status transition from %q to %q is not allowed", currentColumn.Status, targetColumn.Status)
|
||||
}
|
||||
|
||||
_ = card
|
||||
return s.cardRepository.MoveCard(ctx, req.CardID, req.ColumnID, req.NewOrder)
|
||||
}
|
||||
|
||||
// ReorderCards reorders cards within a column
|
||||
func (s *kanbanService) ReorderCards(ctx context.Context, req ReorderCardsRequest) error {
|
||||
func (s *kanbanService) ReorderCards(ctx context.Context, req ReorderCardsRequest, ownerID string) error {
|
||||
s.logger.Info("Reordering cards", map[string]interface{}{
|
||||
"column_id": req.ColumnID,
|
||||
"count": len(req.CardOrders),
|
||||
"owner_id": ownerID,
|
||||
})
|
||||
|
||||
if _, _, err := s.getColumnForOwner(ctx, req.ColumnID, ownerID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.cardRepository.UpdateCardOrder(ctx, req.ColumnID, req.CardOrders)
|
||||
}
|
||||
|
||||
@@ -502,7 +530,7 @@ func (s *kanbanService) ensureDefaultColumns(ctx context.Context, boardID string
|
||||
return nil
|
||||
}
|
||||
|
||||
statuses, err := s.fetchBoardStatuses(ctx)
|
||||
statuses, err := s.resolveWorkflowColumns(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -550,24 +578,26 @@ func dedupeStatuses(statuses []gorules.StatusWithCategory) []gorules.StatusWithC
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *kanbanService) fetchBoardStatuses(ctx context.Context) ([]gorules.StatusWithCategory, error) {
|
||||
defaultStatuses := s.defaultBidWorkflowStatuses()
|
||||
|
||||
if s.goRulesClient == nil || s.goRulesRuleID == "" {
|
||||
return defaultStatuses, nil
|
||||
// resolveWorkflowColumns returns column definitions to persist.
|
||||
// When GoRules is configured, only a successful GoRules response is persisted.
|
||||
// When GoRules is not configured, built-in bid workflow defaults are used (explicit offline mode).
|
||||
func (s *kanbanService) resolveWorkflowColumns(ctx context.Context) ([]gorules.StatusWithCategory, error) {
|
||||
if !s.goRulesEnabled() {
|
||||
s.logger.Info("GoRules not configured; using built-in bid workflow columns", map[string]interface{}{})
|
||||
return s.defaultBidWorkflowStatuses(), nil
|
||||
}
|
||||
|
||||
fromRules, err := s.goRulesClient.EvaluateAvailableStatuses(ctx, "", s.goRulesRuleID)
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to fetch statuses from GoRules, using default bid workflow", map[string]interface{}{
|
||||
s.logger.Warn("GoRules unavailable; workflow columns not persisted", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return defaultStatuses, nil
|
||||
return nil, fmt.Errorf("%w: %v", ErrWorkflowUnavailable, err)
|
||||
}
|
||||
|
||||
normalized := dedupeStatuses(fromRules)
|
||||
if len(normalized) == 0 {
|
||||
return defaultStatuses, nil
|
||||
return nil, fmt.Errorf("%w: empty status list from rules engine", ErrWorkflowUnavailable)
|
||||
}
|
||||
|
||||
sort.SliceStable(normalized, func(i, j int) bool {
|
||||
@@ -577,43 +607,113 @@ func (s *kanbanService) fetchBoardStatuses(ctx context.Context) ([]gorules.Statu
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
func (s *kanbanService) availableStatuses(ctx context.Context, currentStatus string) (map[string]struct{}, error) {
|
||||
allowed := map[string]struct{}{}
|
||||
|
||||
defaultStatuses, err := s.fetchBoardStatuses(ctx)
|
||||
// availableStatuses returns allowed target column statuses for a move.
|
||||
// With GoRules configured, only statuses returned by the rules engine are allowed (strict).
|
||||
// Without GoRules, a linear fallback allows the current stage and the next stage only.
|
||||
func (s *kanbanService) availableStatuses(ctx context.Context, boardID, currentStatus string) (map[string]struct{}, error) {
|
||||
columns, err := s.columnRepository.GetColumnsByBoardID(ctx, boardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, status := range defaultStatuses {
|
||||
allowed[s.normalizeStatus(status.Status)] = struct{}{}
|
||||
}
|
||||
|
||||
if s.goRulesClient == nil || s.goRulesRuleID == "" {
|
||||
return allowed, nil
|
||||
boardStatuses := columnStatusSet(columns)
|
||||
|
||||
if !s.goRulesEnabled() {
|
||||
s.logger.Warn("GoRules not configured; using linear workflow fallback for card moves (current + next stage only)", map[string]interface{}{
|
||||
"board_id": boardID,
|
||||
"current_status": currentStatus,
|
||||
})
|
||||
return s.linearTransitionFallback(columns, currentStatus), nil
|
||||
}
|
||||
|
||||
fromRules, err := s.goRulesClient.EvaluateAvailableStatuses(ctx, currentStatus, s.goRulesRuleID)
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to validate transition with GoRules, using allowed board statuses", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"current_status": currentStatus,
|
||||
})
|
||||
return allowed, nil
|
||||
return nil, fmt.Errorf("%w: %v", ErrWorkflowUnavailable, err)
|
||||
}
|
||||
|
||||
ruleAllowed := map[string]struct{}{}
|
||||
allowed := map[string]struct{}{}
|
||||
for _, item := range fromRules {
|
||||
normalized := s.normalizeStatus(item.Status)
|
||||
if _, exists := allowed[normalized]; exists {
|
||||
ruleAllowed[normalized] = struct{}{}
|
||||
if _, onBoard := boardStatuses[normalized]; onBoard {
|
||||
allowed[normalized] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ruleAllowed) == 0 {
|
||||
return allowed, nil
|
||||
if len(allowed) == 0 {
|
||||
return nil, fmt.Errorf("no allowed transitions from %q per workflow rules", currentStatus)
|
||||
}
|
||||
|
||||
return ruleAllowed, nil
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (s *kanbanService) goRulesEnabled() bool {
|
||||
return s.goRulesClient != nil && strings.TrimSpace(s.goRulesRuleID) != ""
|
||||
}
|
||||
|
||||
func columnStatusSet(columns []Column) map[string]struct{} {
|
||||
set := make(map[string]struct{}, len(columns))
|
||||
for _, col := range columns {
|
||||
set[strings.ToLower(strings.TrimSpace(col.Status))] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// linearTransitionFallback allows staying on the current stage or moving to the next ordered column only.
|
||||
func (s *kanbanService) linearTransitionFallback(columns []Column, currentStatus string) map[string]struct{} {
|
||||
sorted := append([]Column(nil), columns...)
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].Order < sorted[j].Order
|
||||
})
|
||||
|
||||
current := s.normalizeStatus(currentStatus)
|
||||
allowed := map[string]struct{}{current: {}}
|
||||
|
||||
for i, col := range sorted {
|
||||
if s.normalizeStatus(col.Status) != current {
|
||||
continue
|
||||
}
|
||||
if i+1 < len(sorted) {
|
||||
allowed[s.normalizeStatus(sorted[i+1].Status)] = struct{}{}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return allowed
|
||||
}
|
||||
|
||||
func (s *kanbanService) getBoardForOwner(ctx context.Context, boardID, ownerID string) (*Board, error) {
|
||||
board, err := s.boardRepository.GetBoardByID(ctx, boardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if board.UserID != ownerID {
|
||||
return nil, ErrAccessDenied
|
||||
}
|
||||
return board, nil
|
||||
}
|
||||
|
||||
func (s *kanbanService) getColumnForOwner(ctx context.Context, columnID, ownerID string) (*Column, *Board, error) {
|
||||
column, err := s.columnRepository.GetColumnByID(ctx, columnID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
board, err := s.getBoardForOwner(ctx, column.BoardID, ownerID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return column, board, nil
|
||||
}
|
||||
|
||||
func (s *kanbanService) getCardForOwner(ctx context.Context, cardID, ownerID string) (*Card, *Column, *Board, error) {
|
||||
card, err := s.cardRepository.GetCardByID(ctx, cardID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
column, board, err := s.getColumnForOwner(ctx, card.ColumnID, ownerID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return card, column, board, nil
|
||||
}
|
||||
|
||||
func (s *kanbanService) normalizeStatus(status string) string {
|
||||
|
||||
Reference in New Issue
Block a user