1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
class NotesFinder
FETCH_OVERLAP = 5.seconds
# Used to filter Notes
# When used with target_type and target_id this returns notes specifically for the controller
#
# Arguments:
# current_user - which user check authorizations with
# project - which project to look for notes on
# params:
# target_type: string
# target_id: integer
# last_fetched_at: time
# search: string
#
def initialize(project, current_user, params = {})
@project = project
@current_user = current_user
@params = params
end
def execute
notes = init_collection
notes = since_fetch_at(notes)
notes.fresh
end
def target
return @target if defined?(@target)
target_type = @params[:target_type]
target_id = @params[:target_id]
return @target = nil unless target_type && target_id
@target =
if target_type == "commit"
if Ability.allowed?(@current_user, :download_code, @project)
@project.commit(target_id)
end
else
noteables_for_type(target_type).find(target_id)
end
end
private
def init_collection
if target
notes_on_target
elsif target_type
notes_of_target_type
else
notes_of_any_type
end
end
def notes_of_target_type
notes = notes_for_type(target_type)
search(notes)
end
def target_type
@params[:target_type]
end
# rubocop: disable CodeReuse/ActiveRecord
def notes_of_any_type
types = %w(commit issue merge_request snippet)
note_relations = types.map { |t| notes_for_type(t) }
note_relations.map! { |notes| search(notes) }
UnionFinder.new.find_union(note_relations, Note.includes(:author)) # rubocop: disable CodeReuse/Finder
end
# rubocop: enable CodeReuse/ActiveRecord
def noteables_for_type(noteable_type)
case noteable_type
when "issue"
IssuesFinder.new(@current_user, project_id: @project.id).execute # rubocop: disable CodeReuse/Finder
when "merge_request"
MergeRequestsFinder.new(@current_user, project_id: @project.id).execute # rubocop: disable CodeReuse/Finder
when "snippet", "project_snippet"
SnippetsFinder.new(@current_user, project: @project).execute # rubocop: disable CodeReuse/Finder
when "personal_snippet"
PersonalSnippet.all
else
raise "invalid target_type '#{noteable_type}'"
end
end
# rubocop: disable CodeReuse/ActiveRecord
def notes_for_type(noteable_type)
if noteable_type == "commit"
if Ability.allowed?(@current_user, :download_code, @project)
@project.notes.where(noteable_type: 'Commit')
else
Note.none
end
else
finder = noteables_for_type(noteable_type)
@project.notes.where(noteable_type: finder.base_class.name, noteable_id: finder.reorder(nil))
end
end
# rubocop: enable CodeReuse/ActiveRecord
def notes_on_target
if target.respond_to?(:related_notes)
target.related_notes
else
target.notes
end
end
# Searches for notes matching the given query.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
#
def search(notes)
query = @params[:search]
return notes unless query
notes.search(query)
end
# Notes changed since last fetch
# Uses overlapping intervals to avoid worrying about race conditions
def since_fetch_at(notes)
return notes unless @params[:last_fetched_at]
# Default to 0 to remain compatible with old clients
last_fetched_at = Time.at(@params.fetch(:last_fetched_at, 0).to_i)
notes.updated_after(last_fetched_at - FETCH_OVERLAP)
end
end
|