gitea

Зеркало из https://github.com/go-gitea/gitea
Форк
0
/
comment_list.go 
492 строки · 11.3 Кб
1
// Copyright 2018 The Gitea Authors. All rights reserved.
2
// SPDX-License-Identifier: MIT
3

4
package issues
5

6
import (
7
	"context"
8

9
	"code.gitea.io/gitea/models/db"
10
	repo_model "code.gitea.io/gitea/models/repo"
11
	user_model "code.gitea.io/gitea/models/user"
12
	"code.gitea.io/gitea/modules/container"
13
	"code.gitea.io/gitea/modules/log"
14
)
15

16
// CommentList defines a list of comments
17
type CommentList []*Comment
18

19
// LoadPosters loads posters
20
func (comments CommentList) LoadPosters(ctx context.Context) error {
21
	if len(comments) == 0 {
22
		return nil
23
	}
24

25
	posterIDs := container.FilterSlice(comments, func(c *Comment) (int64, bool) {
26
		return c.PosterID, c.Poster == nil && c.PosterID > 0
27
	})
28

29
	posterMaps, err := getPostersByIDs(ctx, posterIDs)
30
	if err != nil {
31
		return err
32
	}
33

34
	for _, comment := range comments {
35
		if comment.Poster == nil {
36
			comment.Poster = getPoster(comment.PosterID, posterMaps)
37
		}
38
	}
39
	return nil
40
}
41

42
func (comments CommentList) getLabelIDs() []int64 {
43
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
44
		return comment.LabelID, comment.LabelID > 0
45
	})
46
}
47

48
func (comments CommentList) loadLabels(ctx context.Context) error {
49
	if len(comments) == 0 {
50
		return nil
51
	}
52

53
	labelIDs := comments.getLabelIDs()
54
	commentLabels := make(map[int64]*Label, len(labelIDs))
55
	left := len(labelIDs)
56
	for left > 0 {
57
		limit := db.DefaultMaxInSize
58
		if left < limit {
59
			limit = left
60
		}
61
		rows, err := db.GetEngine(ctx).
62
			In("id", labelIDs[:limit]).
63
			Rows(new(Label))
64
		if err != nil {
65
			return err
66
		}
67

68
		for rows.Next() {
69
			var label Label
70
			err = rows.Scan(&label)
71
			if err != nil {
72
				_ = rows.Close()
73
				return err
74
			}
75
			commentLabels[label.ID] = &label
76
		}
77
		_ = rows.Close()
78
		left -= limit
79
		labelIDs = labelIDs[limit:]
80
	}
81

82
	for _, comment := range comments {
83
		comment.Label = commentLabels[comment.ID]
84
	}
85
	return nil
86
}
87

88
func (comments CommentList) getMilestoneIDs() []int64 {
89
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
90
		return comment.MilestoneID, comment.MilestoneID > 0
91
	})
92
}
93

94
func (comments CommentList) loadMilestones(ctx context.Context) error {
95
	if len(comments) == 0 {
96
		return nil
97
	}
98

99
	milestoneIDs := comments.getMilestoneIDs()
100
	if len(milestoneIDs) == 0 {
101
		return nil
102
	}
103

104
	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
105
	left := len(milestoneIDs)
106
	for left > 0 {
107
		limit := db.DefaultMaxInSize
108
		if left < limit {
109
			limit = left
110
		}
111
		err := db.GetEngine(ctx).
112
			In("id", milestoneIDs[:limit]).
113
			Find(&milestoneMaps)
114
		if err != nil {
115
			return err
116
		}
117
		left -= limit
118
		milestoneIDs = milestoneIDs[limit:]
119
	}
120

121
	for _, issue := range comments {
122
		issue.Milestone = milestoneMaps[issue.MilestoneID]
123
	}
124
	return nil
125
}
126

127
func (comments CommentList) getOldMilestoneIDs() []int64 {
128
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
129
		return comment.OldMilestoneID, comment.OldMilestoneID > 0
130
	})
131
}
132

133
func (comments CommentList) loadOldMilestones(ctx context.Context) error {
134
	if len(comments) == 0 {
135
		return nil
136
	}
137

138
	milestoneIDs := comments.getOldMilestoneIDs()
139
	if len(milestoneIDs) == 0 {
140
		return nil
141
	}
142

143
	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
144
	left := len(milestoneIDs)
145
	for left > 0 {
146
		limit := db.DefaultMaxInSize
147
		if left < limit {
148
			limit = left
149
		}
150
		err := db.GetEngine(ctx).
151
			In("id", milestoneIDs[:limit]).
152
			Find(&milestoneMaps)
153
		if err != nil {
154
			return err
155
		}
156
		left -= limit
157
		milestoneIDs = milestoneIDs[limit:]
158
	}
159

160
	for _, issue := range comments {
161
		issue.OldMilestone = milestoneMaps[issue.MilestoneID]
162
	}
163
	return nil
164
}
165

166
func (comments CommentList) getAssigneeIDs() []int64 {
167
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
168
		return comment.AssigneeID, comment.AssigneeID > 0
169
	})
170
}
171

172
func (comments CommentList) loadAssignees(ctx context.Context) error {
173
	if len(comments) == 0 {
174
		return nil
175
	}
176

177
	assigneeIDs := comments.getAssigneeIDs()
178
	assignees := make(map[int64]*user_model.User, len(assigneeIDs))
179
	left := len(assigneeIDs)
180
	for left > 0 {
181
		limit := db.DefaultMaxInSize
182
		if left < limit {
183
			limit = left
184
		}
185
		rows, err := db.GetEngine(ctx).
186
			In("id", assigneeIDs[:limit]).
187
			Rows(new(user_model.User))
188
		if err != nil {
189
			return err
190
		}
191

192
		for rows.Next() {
193
			var user user_model.User
194
			err = rows.Scan(&user)
195
			if err != nil {
196
				rows.Close()
197
				return err
198
			}
199

200
			assignees[user.ID] = &user
201
		}
202
		_ = rows.Close()
203

204
		left -= limit
205
		assigneeIDs = assigneeIDs[limit:]
206
	}
207

208
	for _, comment := range comments {
209
		comment.Assignee = assignees[comment.AssigneeID]
210
		if comment.Assignee == nil {
211
			comment.AssigneeID = user_model.GhostUserID
212
			comment.Assignee = user_model.NewGhostUser()
213
		}
214
	}
215
	return nil
216
}
217

218
// getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
219
func (comments CommentList) getIssueIDs() []int64 {
220
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
221
		return comment.IssueID, comment.Issue == nil
222
	})
223
}
224

225
// Issues returns all the issues of comments
226
func (comments CommentList) Issues() IssueList {
227
	issues := make(map[int64]*Issue, len(comments))
228
	for _, comment := range comments {
229
		if comment.Issue != nil {
230
			if _, ok := issues[comment.Issue.ID]; !ok {
231
				issues[comment.Issue.ID] = comment.Issue
232
			}
233
		}
234
	}
235

236
	issueList := make([]*Issue, 0, len(issues))
237
	for _, issue := range issues {
238
		issueList = append(issueList, issue)
239
	}
240
	return issueList
241
}
242

243
// LoadIssues loads issues of comments
244
func (comments CommentList) LoadIssues(ctx context.Context) error {
245
	if len(comments) == 0 {
246
		return nil
247
	}
248

249
	issueIDs := comments.getIssueIDs()
250
	issues := make(map[int64]*Issue, len(issueIDs))
251
	left := len(issueIDs)
252
	for left > 0 {
253
		limit := db.DefaultMaxInSize
254
		if left < limit {
255
			limit = left
256
		}
257
		rows, err := db.GetEngine(ctx).
258
			In("id", issueIDs[:limit]).
259
			Rows(new(Issue))
260
		if err != nil {
261
			return err
262
		}
263

264
		for rows.Next() {
265
			var issue Issue
266
			err = rows.Scan(&issue)
267
			if err != nil {
268
				rows.Close()
269
				return err
270
			}
271

272
			issues[issue.ID] = &issue
273
		}
274
		_ = rows.Close()
275

276
		left -= limit
277
		issueIDs = issueIDs[limit:]
278
	}
279

280
	for _, comment := range comments {
281
		if comment.Issue == nil {
282
			comment.Issue = issues[comment.IssueID]
283
		}
284
	}
285
	return nil
286
}
287

288
func (comments CommentList) getDependentIssueIDs() []int64 {
289
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
290
		if comment.DependentIssue != nil {
291
			return 0, false
292
		}
293
		return comment.DependentIssueID, comment.DependentIssueID > 0
294
	})
295
}
296

297
func (comments CommentList) loadDependentIssues(ctx context.Context) error {
298
	if len(comments) == 0 {
299
		return nil
300
	}
301

302
	e := db.GetEngine(ctx)
303
	issueIDs := comments.getDependentIssueIDs()
304
	issues := make(map[int64]*Issue, len(issueIDs))
305
	left := len(issueIDs)
306
	for left > 0 {
307
		limit := db.DefaultMaxInSize
308
		if left < limit {
309
			limit = left
310
		}
311
		rows, err := e.
312
			In("id", issueIDs[:limit]).
313
			Rows(new(Issue))
314
		if err != nil {
315
			return err
316
		}
317

318
		for rows.Next() {
319
			var issue Issue
320
			err = rows.Scan(&issue)
321
			if err != nil {
322
				_ = rows.Close()
323
				return err
324
			}
325

326
			issues[issue.ID] = &issue
327
		}
328
		_ = rows.Close()
329

330
		left -= limit
331
		issueIDs = issueIDs[limit:]
332
	}
333

334
	for _, comment := range comments {
335
		if comment.DependentIssue == nil {
336
			comment.DependentIssue = issues[comment.DependentIssueID]
337
			if comment.DependentIssue != nil {
338
				if err := comment.DependentIssue.LoadRepo(ctx); err != nil {
339
					return err
340
				}
341
			}
342
		}
343
	}
344
	return nil
345
}
346

347
// getAttachmentCommentIDs only return the comment ids which possibly has attachments
348
func (comments CommentList) getAttachmentCommentIDs() []int64 {
349
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
350
		return comment.ID, comment.Type.HasAttachmentSupport()
351
	})
352
}
353

354
// LoadAttachmentsByIssue loads attachments by issue id
355
func (comments CommentList) LoadAttachmentsByIssue(ctx context.Context) error {
356
	if len(comments) == 0 {
357
		return nil
358
	}
359

360
	attachments := make([]*repo_model.Attachment, 0, len(comments)/2)
361
	if err := db.GetEngine(ctx).Where("issue_id=? AND comment_id>0", comments[0].IssueID).Find(&attachments); err != nil {
362
		return err
363
	}
364

365
	commentAttachmentsMap := make(map[int64][]*repo_model.Attachment, len(comments))
366
	for _, attach := range attachments {
367
		commentAttachmentsMap[attach.CommentID] = append(commentAttachmentsMap[attach.CommentID], attach)
368
	}
369

370
	for _, comment := range comments {
371
		comment.Attachments = commentAttachmentsMap[comment.ID]
372
	}
373
	return nil
374
}
375

376
// LoadAttachments loads attachments
377
func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
378
	if len(comments) == 0 {
379
		return nil
380
	}
381

382
	attachments := make(map[int64][]*repo_model.Attachment, len(comments))
383
	commentsIDs := comments.getAttachmentCommentIDs()
384
	left := len(commentsIDs)
385
	for left > 0 {
386
		limit := db.DefaultMaxInSize
387
		if left < limit {
388
			limit = left
389
		}
390
		rows, err := db.GetEngine(ctx).
391
			In("comment_id", commentsIDs[:limit]).
392
			Rows(new(repo_model.Attachment))
393
		if err != nil {
394
			return err
395
		}
396

397
		for rows.Next() {
398
			var attachment repo_model.Attachment
399
			err = rows.Scan(&attachment)
400
			if err != nil {
401
				_ = rows.Close()
402
				return err
403
			}
404
			attachments[attachment.CommentID] = append(attachments[attachment.CommentID], &attachment)
405
		}
406

407
		_ = rows.Close()
408
		left -= limit
409
		commentsIDs = commentsIDs[limit:]
410
	}
411

412
	for _, comment := range comments {
413
		comment.Attachments = attachments[comment.ID]
414
	}
415
	return nil
416
}
417

418
func (comments CommentList) getReviewIDs() []int64 {
419
	return container.FilterSlice(comments, func(comment *Comment) (int64, bool) {
420
		return comment.ReviewID, comment.ReviewID > 0
421
	})
422
}
423

424
func (comments CommentList) loadReviews(ctx context.Context) error {
425
	if len(comments) == 0 {
426
		return nil
427
	}
428

429
	reviewIDs := comments.getReviewIDs()
430
	reviews := make(map[int64]*Review, len(reviewIDs))
431
	if err := db.GetEngine(ctx).In("id", reviewIDs).Find(&reviews); err != nil {
432
		return err
433
	}
434

435
	for _, comment := range comments {
436
		comment.Review = reviews[comment.ReviewID]
437
		if comment.Review == nil {
438
			// review request which has been replaced by actual reviews doesn't exist in database anymore, so don't log errors for them.
439
			if comment.ReviewID > 0 && comment.Type != CommentTypeReviewRequest {
440
				log.Error("comment with review id [%d] but has no review record", comment.ReviewID)
441
			}
442
			continue
443
		}
444

445
		// If the comment dismisses a review, we need to load the reviewer to show whose review has been dismissed.
446
		// Otherwise, the reviewer is the poster of the comment, so we don't need to load it.
447
		if comment.Type == CommentTypeDismissReview {
448
			if err := comment.Review.LoadReviewer(ctx); err != nil {
449
				return err
450
			}
451
		}
452
	}
453
	return nil
454
}
455

456
// LoadAttributes loads attributes of the comments, except for attachments and
457
// comments
458
func (comments CommentList) LoadAttributes(ctx context.Context) (err error) {
459
	if err = comments.LoadPosters(ctx); err != nil {
460
		return err
461
	}
462

463
	if err = comments.loadLabels(ctx); err != nil {
464
		return err
465
	}
466

467
	if err = comments.loadMilestones(ctx); err != nil {
468
		return err
469
	}
470

471
	if err = comments.loadOldMilestones(ctx); err != nil {
472
		return err
473
	}
474

475
	if err = comments.loadAssignees(ctx); err != nil {
476
		return err
477
	}
478

479
	if err = comments.LoadAttachments(ctx); err != nil {
480
		return err
481
	}
482

483
	if err = comments.loadReviews(ctx); err != nil {
484
		return err
485
	}
486

487
	if err = comments.LoadIssues(ctx); err != nil {
488
		return err
489
	}
490

491
	return comments.loadDependentIssues(ctx)
492
}
493

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.