diff --git a/app/api/controller/pullreq/pr_find.go b/app/api/controller/pullreq/pr_find.go index 5939f6fe6..7e97133ba 100644 --- a/app/api/controller/pullreq/pr_find.go +++ b/app/api/controller/pullreq/pr_find.go @@ -48,6 +48,11 @@ func (c *Controller) Find( return nil, err } + err = c.labelSvc.Backfill(ctx, pr) + if err != nil { + return nil, fmt.Errorf("failed to backfill labels assigned to pull request: %w", err) + } + if err := c.backfillStats(ctx, repo, pr); err != nil { log.Ctx(ctx).Warn().Err(err).Msg("failed to backfill PR stats") } diff --git a/app/api/controller/pullreq/pr_list.go b/app/api/controller/pullreq/pr_list.go index 10e3ba98e..f558ff9e0 100644 --- a/app/api/controller/pullreq/pr_list.go +++ b/app/api/controller/pullreq/pr_list.go @@ -60,6 +60,11 @@ func (c *Controller) List( return fmt.Errorf("failed to list pull requests: %w", err) } + err := c.labelSvc.BackfillMany(ctx, list) + if err != nil { + return fmt.Errorf("failed to backfill labels assigned to pull requests: %w", err) + } + if filter.Page == 1 && len(list) < filter.Size { count = int64(len(list)) return nil diff --git a/app/services/label/label_pullreq.go b/app/services/label/label_pullreq.go index 4a328a483..ace666872 100644 --- a/app/services/label/label_pullreq.go +++ b/app/services/label/label_pullreq.go @@ -253,8 +253,7 @@ func (s *Service) ListPullReqLabels( scopeLabelsMap := make(map[int64]*types.ScopeData) - pullreqAssignments, err := s.pullReqLabelAssignmentStore.ListAssigned( - ctx, pullreqID) + pullreqAssignments, err := s.pullReqLabelAssignmentStore.ListAssigned(ctx, pullreqID) if err != nil { return nil, 0, fmt.Errorf("failed to list labels assigned to pullreq: %w", err) } @@ -313,6 +312,43 @@ func (s *Service) ListPullReqLabels( return createScopeLabels(allAssignments, scopeLabelsMap), total, nil } +func (s *Service) Backfill( + ctx context.Context, + pullreq *types.PullReq, +) error { + pullreqAssignments, err := s.pullReqLabelAssignmentStore.ListAssignedByPullreqIDs( + ctx, []int64{pullreq.ID}) + if err != nil { + return fmt.Errorf("failed to list labels assigned to pullreq: %w", err) + } + + pullreq.Labels = pullreqAssignments[pullreq.ID] + + return nil +} + +func (s *Service) BackfillMany( + ctx context.Context, + pullreqs []*types.PullReq, +) error { + pullreqIDs := make([]int64, len(pullreqs)) + for i, pr := range pullreqs { + pullreqIDs[i] = pr.ID + } + + pullreqAssignments, err := s.pullReqLabelAssignmentStore.ListAssignedByPullreqIDs( + ctx, pullreqIDs) + if err != nil { + return fmt.Errorf("failed to list labels assigned to pullreq: %w", err) + } + + for _, pullreq := range pullreqs { + pullreq.Labels = pullreqAssignments[pullreq.ID] + } + + return nil +} + func populateScopeLabelsMap( assignments []*types.LabelAssignment, scopeLabelsMap map[int64]*types.ScopeData, diff --git a/app/store/database.go b/app/store/database.go index ed9bfa273..028e53fda 100644 --- a/app/store/database.go +++ b/app/store/database.go @@ -1045,7 +1045,10 @@ type ( Unassign(ctx context.Context, pullreqID int64, labelID int64) error // ListAssigned list labels assigned to a specified pullreq. - ListAssigned(ctx context.Context, pullreqID int64) (map[int64]*types.LabelAssignment, error) + ListAssigned( + ctx context.Context, + pullreqID int64, + ) (map[int64]*types.LabelAssignment, error) // Find finds a label assigned to a pullreq with a specified id. FindByLabelID( @@ -1056,5 +1059,11 @@ type ( // FindValueByLabelID finds a value assigned to a pullreq label. FindValueByLabelID(ctx context.Context, labelID int64) (*types.LabelValue, error) + + // ListAssignedByPullreqIDs list labels assigned to specified pullreqs. + ListAssignedByPullreqIDs( + ctx context.Context, + pullreqIDs []int64, + ) (map[int64][]*types.LabelPullReqAssignmentInfo, error) } ) diff --git a/app/store/database/label_pullreq.go b/app/store/database/label_pullreq.go index 7dd4d51de..0b15136e0 100644 --- a/app/store/database/label_pullreq.go +++ b/app/store/database/label_pullreq.go @@ -21,9 +21,12 @@ import ( "github.com/harness/gitness/store/database" "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" + "github.com/Masterminds/squirrel" "github.com/guregu/null" "github.com/jmoiron/sqlx" + "github.com/pkg/errors" ) var _ store.PullReqLabelAssignmentStore = (*pullReqLabelStore)(nil) @@ -48,6 +51,16 @@ type pullReqLabel struct { UpdatedBy int64 `db:"pullreq_label_updated_by"` } +type pullReqAssignmentInfo struct { + PullReqID int64 `db:"pullreq_label_pullreq_id"` + LabelID int64 `db:"label_id"` + LabelKey string `db:"label_key"` + LabelColor enum.LabelColor `db:"label_color"` + ValueCount int64 `db:"label_value_count"` + Value null.String `db:"label_value_value"` + ValueColor null.String `db:"label_value_color"` +} + const ( pullReqLabelColumns = ` pullreq_label_pullreq_id @@ -130,7 +143,7 @@ func (s *pullReqLabelStore) ListAssigned( ctx context.Context, pullreqID int64, ) (map[int64]*types.LabelAssignment, error) { - const sqlQueryAssigned = ` + const sqlQuery = ` SELECT label_id ,label_repo_id @@ -143,10 +156,10 @@ func (s *pullReqLabelStore) ListAssigned( ,label_value_color ,label_scope ,label_type - FROM pullreq_labels prl - INNER JOIN labels l ON prl.pullreq_label_label_id = l.label_id - LEFT JOIN label_values lv ON prl.pullreq_label_label_value_id = lv.label_value_id - WHERE prl.pullreq_label_pullreq_id = $1` + FROM pullreq_labels + INNER JOIN labels ON pullreq_label_label_id = label_id + LEFT JOIN label_values ON pullreq_label_label_value_id = label_value_id + WHERE pullreq_label_pullreq_id = $1` db := dbtx.GetAccessor(ctx, s.db) @@ -154,8 +167,8 @@ func (s *pullReqLabelStore) ListAssigned( labelInfo labelValueInfo } - if err := db.SelectContext(ctx, &dst, sqlQueryAssigned, pullreqID); err != nil { - return nil, database.ProcessSQLErrorf(ctx, err, "failed to find label") + if err := db.SelectContext(ctx, &dst, sqlQuery, pullreqID); err != nil { + return nil, database.ProcessSQLErrorf(ctx, err, "failed to list assigned label") } ret := make(map[int64]*types.LabelAssignment, len(dst)) @@ -171,6 +184,39 @@ func (s *pullReqLabelStore) ListAssigned( return ret, nil } +func (s *pullReqLabelStore) ListAssignedByPullreqIDs( + ctx context.Context, + pullreqIDs []int64, +) (map[int64][]*types.LabelPullReqAssignmentInfo, error) { + stmt := database.Builder.Select(` + pullreq_label_pullreq_id + ,label_id + ,label_key + ,label_color + ,label_value_count + ,label_value_value + ,label_value_color + `). + From("pullreq_labels"). + InnerJoin("labels ON pullreq_label_label_id = label_id"). + LeftJoin("label_values ON pullreq_label_label_value_id = label_value_id"). + Where(squirrel.Eq{"pullreq_label_pullreq_id": pullreqIDs}) + + sql, args, err := stmt.ToSql() + if err != nil { + return nil, errors.Wrap(err, "Failed to convert query to sql") + } + + db := dbtx.GetAccessor(ctx, s.db) + + var dst []*pullReqAssignmentInfo + if err := db.SelectContext(ctx, &dst, sql, args...); err != nil { + return nil, database.ProcessSQLErrorf(ctx, err, "failed to list assigned label") + } + + return mapPullReqAssignmentInfos(dst), nil +} + func (s *pullReqLabelStore) FindValueByLabelID( ctx context.Context, labelID int64, @@ -213,3 +259,27 @@ func mapPullReqLabel(lbl *pullReqLabel) *types.PullReqLabel { UpdatedBy: lbl.UpdatedBy, } } + +func mapPullReqAssignmentInfo(lbl *pullReqAssignmentInfo) *types.LabelPullReqAssignmentInfo { + return &types.LabelPullReqAssignmentInfo{ + PullReqID: lbl.PullReqID, + LabelID: lbl.LabelID, + LabelKey: lbl.LabelKey, + LabelColor: lbl.LabelColor, + ValueCount: lbl.ValueCount, + Value: lbl.Value.Ptr(), + ValueColor: lbl.ValueColor.Ptr(), + } +} + +func mapPullReqAssignmentInfos( + dbLabels []*pullReqAssignmentInfo, +) map[int64][]*types.LabelPullReqAssignmentInfo { + result := make(map[int64][]*types.LabelPullReqAssignmentInfo) + + for _, lbl := range dbLabels { + result[lbl.PullReqID] = append(result[lbl.PullReqID], mapPullReqAssignmentInfo(lbl)) + } + + return result +} diff --git a/types/label.go b/types/label.go index 5c6906133..77d1f26ad 100644 --- a/types/label.go +++ b/types/label.go @@ -94,6 +94,16 @@ type LabelAssignment struct { Values []*LabelValueInfo `json:"values,omitempty"` // query param ?assignable=true } +type LabelPullReqAssignmentInfo struct { + PullReqID int64 `json:"-"` + LabelID int64 `json:"id"` + LabelKey string `json:"key"` + LabelColor enum.LabelColor `json:"color,omitempty"` + ValueCount int64 `json:"value_count"` + Value *string `json:"value,omitempty"` + ValueColor *string `json:"value_color,omitempty"` +} + type ScopeData struct { // Scope = 0 is repo, scope >= 1 is a depth level of a space Scope int64 `json:"scope"` diff --git a/types/pullreq.go b/types/pullreq.go index 1595e6d77..91e160503 100644 --- a/types/pullreq.go +++ b/types/pullreq.go @@ -60,6 +60,8 @@ type PullReq struct { Author PrincipalInfo `json:"author"` Merger *PrincipalInfo `json:"merger"` Stats PullReqStats `json:"stats"` + + Labels []*LabelPullReqAssignmentInfo `json:"labels,omitempty"` } // DiffStats shows total number of commits and modified files.