You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

comment_list.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package issues
  4. import (
  5. "context"
  6. "code.gitea.io/gitea/models/db"
  7. repo_model "code.gitea.io/gitea/models/repo"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/container"
  10. )
  11. // CommentList defines a list of comments
  12. type CommentList []*Comment
  13. func (comments CommentList) getPosterIDs() []int64 {
  14. posterIDs := make(container.Set[int64], len(comments))
  15. for _, comment := range comments {
  16. posterIDs.Add(comment.PosterID)
  17. }
  18. return posterIDs.Values()
  19. }
  20. // LoadPosters loads posters
  21. func (comments CommentList) LoadPosters(ctx context.Context) error {
  22. if len(comments) == 0 {
  23. return nil
  24. }
  25. posterMaps, err := getPosters(ctx, comments.getPosterIDs())
  26. if err != nil {
  27. return err
  28. }
  29. for _, comment := range comments {
  30. comment.Poster = getPoster(comment.PosterID, posterMaps)
  31. }
  32. return nil
  33. }
  34. func (comments CommentList) getCommentIDs() []int64 {
  35. ids := make([]int64, 0, len(comments))
  36. for _, comment := range comments {
  37. ids = append(ids, comment.ID)
  38. }
  39. return ids
  40. }
  41. func (comments CommentList) getLabelIDs() []int64 {
  42. ids := make(container.Set[int64], len(comments))
  43. for _, comment := range comments {
  44. ids.Add(comment.LabelID)
  45. }
  46. return ids.Values()
  47. }
  48. func (comments CommentList) loadLabels(ctx context.Context) error { //nolint
  49. if len(comments) == 0 {
  50. return nil
  51. }
  52. labelIDs := comments.getLabelIDs()
  53. commentLabels := make(map[int64]*Label, len(labelIDs))
  54. left := len(labelIDs)
  55. for left > 0 {
  56. limit := db.DefaultMaxInSize
  57. if left < limit {
  58. limit = left
  59. }
  60. rows, err := db.GetEngine(ctx).
  61. In("id", labelIDs[:limit]).
  62. Rows(new(Label))
  63. if err != nil {
  64. return err
  65. }
  66. for rows.Next() {
  67. var label Label
  68. err = rows.Scan(&label)
  69. if err != nil {
  70. _ = rows.Close()
  71. return err
  72. }
  73. commentLabels[label.ID] = &label
  74. }
  75. _ = rows.Close()
  76. left -= limit
  77. labelIDs = labelIDs[limit:]
  78. }
  79. for _, comment := range comments {
  80. comment.Label = commentLabels[comment.ID]
  81. }
  82. return nil
  83. }
  84. func (comments CommentList) getMilestoneIDs() []int64 {
  85. ids := make(container.Set[int64], len(comments))
  86. for _, comment := range comments {
  87. ids.Add(comment.MilestoneID)
  88. }
  89. return ids.Values()
  90. }
  91. func (comments CommentList) loadMilestones(ctx context.Context) error {
  92. if len(comments) == 0 {
  93. return nil
  94. }
  95. milestoneIDs := comments.getMilestoneIDs()
  96. if len(milestoneIDs) == 0 {
  97. return nil
  98. }
  99. milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
  100. left := len(milestoneIDs)
  101. for left > 0 {
  102. limit := db.DefaultMaxInSize
  103. if left < limit {
  104. limit = left
  105. }
  106. err := db.GetEngine(ctx).
  107. In("id", milestoneIDs[:limit]).
  108. Find(&milestoneMaps)
  109. if err != nil {
  110. return err
  111. }
  112. left -= limit
  113. milestoneIDs = milestoneIDs[limit:]
  114. }
  115. for _, issue := range comments {
  116. issue.Milestone = milestoneMaps[issue.MilestoneID]
  117. }
  118. return nil
  119. }
  120. func (comments CommentList) getOldMilestoneIDs() []int64 {
  121. ids := make(container.Set[int64], len(comments))
  122. for _, comment := range comments {
  123. ids.Add(comment.OldMilestoneID)
  124. }
  125. return ids.Values()
  126. }
  127. func (comments CommentList) loadOldMilestones(ctx context.Context) error {
  128. if len(comments) == 0 {
  129. return nil
  130. }
  131. milestoneIDs := comments.getOldMilestoneIDs()
  132. if len(milestoneIDs) == 0 {
  133. return nil
  134. }
  135. milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs))
  136. left := len(milestoneIDs)
  137. for left > 0 {
  138. limit := db.DefaultMaxInSize
  139. if left < limit {
  140. limit = left
  141. }
  142. err := db.GetEngine(ctx).
  143. In("id", milestoneIDs[:limit]).
  144. Find(&milestoneMaps)
  145. if err != nil {
  146. return err
  147. }
  148. left -= limit
  149. milestoneIDs = milestoneIDs[limit:]
  150. }
  151. for _, issue := range comments {
  152. issue.OldMilestone = milestoneMaps[issue.MilestoneID]
  153. }
  154. return nil
  155. }
  156. func (comments CommentList) getAssigneeIDs() []int64 {
  157. ids := make(container.Set[int64], len(comments))
  158. for _, comment := range comments {
  159. ids.Add(comment.AssigneeID)
  160. }
  161. return ids.Values()
  162. }
  163. func (comments CommentList) loadAssignees(ctx context.Context) error {
  164. if len(comments) == 0 {
  165. return nil
  166. }
  167. assigneeIDs := comments.getAssigneeIDs()
  168. assignees := make(map[int64]*user_model.User, len(assigneeIDs))
  169. left := len(assigneeIDs)
  170. for left > 0 {
  171. limit := db.DefaultMaxInSize
  172. if left < limit {
  173. limit = left
  174. }
  175. rows, err := db.GetEngine(ctx).
  176. In("id", assigneeIDs[:limit]).
  177. Rows(new(user_model.User))
  178. if err != nil {
  179. return err
  180. }
  181. for rows.Next() {
  182. var user user_model.User
  183. err = rows.Scan(&user)
  184. if err != nil {
  185. rows.Close()
  186. return err
  187. }
  188. assignees[user.ID] = &user
  189. }
  190. _ = rows.Close()
  191. left -= limit
  192. assigneeIDs = assigneeIDs[limit:]
  193. }
  194. for _, comment := range comments {
  195. comment.Assignee = assignees[comment.AssigneeID]
  196. }
  197. return nil
  198. }
  199. // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
  200. func (comments CommentList) getIssueIDs() []int64 {
  201. ids := make(container.Set[int64], len(comments))
  202. for _, comment := range comments {
  203. if comment.Issue != nil {
  204. continue
  205. }
  206. ids.Add(comment.IssueID)
  207. }
  208. return ids.Values()
  209. }
  210. // Issues returns all the issues of comments
  211. func (comments CommentList) Issues() IssueList {
  212. issues := make(map[int64]*Issue, len(comments))
  213. for _, comment := range comments {
  214. if comment.Issue != nil {
  215. if _, ok := issues[comment.Issue.ID]; !ok {
  216. issues[comment.Issue.ID] = comment.Issue
  217. }
  218. }
  219. }
  220. issueList := make([]*Issue, 0, len(issues))
  221. for _, issue := range issues {
  222. issueList = append(issueList, issue)
  223. }
  224. return issueList
  225. }
  226. // LoadIssues loads issues of comments
  227. func (comments CommentList) LoadIssues(ctx context.Context) error {
  228. if len(comments) == 0 {
  229. return nil
  230. }
  231. issueIDs := comments.getIssueIDs()
  232. issues := make(map[int64]*Issue, len(issueIDs))
  233. left := len(issueIDs)
  234. for left > 0 {
  235. limit := db.DefaultMaxInSize
  236. if left < limit {
  237. limit = left
  238. }
  239. rows, err := db.GetEngine(ctx).
  240. In("id", issueIDs[:limit]).
  241. Rows(new(Issue))
  242. if err != nil {
  243. return err
  244. }
  245. for rows.Next() {
  246. var issue Issue
  247. err = rows.Scan(&issue)
  248. if err != nil {
  249. rows.Close()
  250. return err
  251. }
  252. issues[issue.ID] = &issue
  253. }
  254. _ = rows.Close()
  255. left -= limit
  256. issueIDs = issueIDs[limit:]
  257. }
  258. for _, comment := range comments {
  259. if comment.Issue == nil {
  260. comment.Issue = issues[comment.IssueID]
  261. }
  262. }
  263. return nil
  264. }
  265. func (comments CommentList) getDependentIssueIDs() []int64 {
  266. ids := make(container.Set[int64], len(comments))
  267. for _, comment := range comments {
  268. if comment.DependentIssue != nil {
  269. continue
  270. }
  271. ids.Add(comment.DependentIssueID)
  272. }
  273. return ids.Values()
  274. }
  275. func (comments CommentList) loadDependentIssues(ctx context.Context) error {
  276. if len(comments) == 0 {
  277. return nil
  278. }
  279. e := db.GetEngine(ctx)
  280. issueIDs := comments.getDependentIssueIDs()
  281. issues := make(map[int64]*Issue, len(issueIDs))
  282. left := len(issueIDs)
  283. for left > 0 {
  284. limit := db.DefaultMaxInSize
  285. if left < limit {
  286. limit = left
  287. }
  288. rows, err := e.
  289. In("id", issueIDs[:limit]).
  290. Rows(new(Issue))
  291. if err != nil {
  292. return err
  293. }
  294. for rows.Next() {
  295. var issue Issue
  296. err = rows.Scan(&issue)
  297. if err != nil {
  298. _ = rows.Close()
  299. return err
  300. }
  301. issues[issue.ID] = &issue
  302. }
  303. _ = rows.Close()
  304. left -= limit
  305. issueIDs = issueIDs[limit:]
  306. }
  307. for _, comment := range comments {
  308. if comment.DependentIssue == nil {
  309. comment.DependentIssue = issues[comment.DependentIssueID]
  310. if comment.DependentIssue != nil {
  311. if err := comment.DependentIssue.LoadRepo(ctx); err != nil {
  312. return err
  313. }
  314. }
  315. }
  316. }
  317. return nil
  318. }
  319. // LoadAttachments loads attachments
  320. func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
  321. if len(comments) == 0 {
  322. return nil
  323. }
  324. attachments := make(map[int64][]*repo_model.Attachment, len(comments))
  325. commentsIDs := comments.getCommentIDs()
  326. left := len(commentsIDs)
  327. for left > 0 {
  328. limit := db.DefaultMaxInSize
  329. if left < limit {
  330. limit = left
  331. }
  332. rows, err := db.GetEngine(ctx).Table("attachment").
  333. Join("INNER", "comment", "comment.id = attachment.comment_id").
  334. In("comment.id", commentsIDs[:limit]).
  335. Rows(new(repo_model.Attachment))
  336. if err != nil {
  337. return err
  338. }
  339. for rows.Next() {
  340. var attachment repo_model.Attachment
  341. err = rows.Scan(&attachment)
  342. if err != nil {
  343. _ = rows.Close()
  344. return err
  345. }
  346. attachments[attachment.CommentID] = append(attachments[attachment.CommentID], &attachment)
  347. }
  348. _ = rows.Close()
  349. left -= limit
  350. commentsIDs = commentsIDs[limit:]
  351. }
  352. for _, comment := range comments {
  353. comment.Attachments = attachments[comment.ID]
  354. }
  355. return nil
  356. }
  357. func (comments CommentList) getReviewIDs() []int64 {
  358. ids := make(container.Set[int64], len(comments))
  359. for _, comment := range comments {
  360. ids.Add(comment.ReviewID)
  361. }
  362. return ids.Values()
  363. }
  364. func (comments CommentList) loadReviews(ctx context.Context) error { //nolint
  365. if len(comments) == 0 {
  366. return nil
  367. }
  368. reviewIDs := comments.getReviewIDs()
  369. reviews := make(map[int64]*Review, len(reviewIDs))
  370. left := len(reviewIDs)
  371. for left > 0 {
  372. limit := db.DefaultMaxInSize
  373. if left < limit {
  374. limit = left
  375. }
  376. rows, err := db.GetEngine(ctx).
  377. In("id", reviewIDs[:limit]).
  378. Rows(new(Review))
  379. if err != nil {
  380. return err
  381. }
  382. for rows.Next() {
  383. var review Review
  384. err = rows.Scan(&review)
  385. if err != nil {
  386. _ = rows.Close()
  387. return err
  388. }
  389. reviews[review.ID] = &review
  390. }
  391. _ = rows.Close()
  392. left -= limit
  393. reviewIDs = reviewIDs[limit:]
  394. }
  395. for _, comment := range comments {
  396. comment.Review = reviews[comment.ReviewID]
  397. }
  398. return nil
  399. }
  400. // loadAttributes loads all attributes
  401. func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
  402. if err = comments.LoadPosters(ctx); err != nil {
  403. return
  404. }
  405. if err = comments.loadLabels(ctx); err != nil {
  406. return
  407. }
  408. if err = comments.loadMilestones(ctx); err != nil {
  409. return
  410. }
  411. if err = comments.loadOldMilestones(ctx); err != nil {
  412. return
  413. }
  414. if err = comments.loadAssignees(ctx); err != nil {
  415. return
  416. }
  417. if err = comments.LoadAttachments(ctx); err != nil {
  418. return
  419. }
  420. if err = comments.loadReviews(ctx); err != nil {
  421. return
  422. }
  423. if err = comments.LoadIssues(ctx); err != nil {
  424. return
  425. }
  426. if err = comments.loadDependentIssues(ctx); err != nil {
  427. return
  428. }
  429. return nil
  430. }
  431. // LoadAttributes loads attributes of the comments, except for attachments and
  432. // comments
  433. func (comments CommentList) LoadAttributes() error {
  434. return comments.loadAttributes(db.DefaultContext)
  435. }